Go by Example

Temporary Files and Directories

os.CreateTemp and os.MkdirTemp for creating temporary files and directories, deferred cleanup, and t.TempDir in tests.

os.CreateTemp and os.MkdirTemp create temporary files and directories with unique, non-colliding names. They are the safe replacement for manual temp-file construction, which is vulnerable to race conditions.

os.CreateTemp creates a new file in the system temp directory (or a specified dir) with a unique name built from the given pattern. The returned *os.File is open for reading and writing. Clean up with os.Remove when done.

package main
 
import (
    "fmt"
    "os"
)
 
func main() {
    // "" uses os.TempDir(); the name will be something like /tmp/example-1234567890
    f, err := os.CreateTemp("", "example-")
    if err != nil {
        panic(err)
    }
    defer os.Remove(f.Name()) // clean up on exit
    defer f.Close()
 
    fmt.Println("temp file:", f.Name())
 
    _, err = f.WriteString("temporary content\n")
    if err != nil {
        panic(err)
    }
}

os.MkdirTemp creates a temporary directory. Use it when you need a temporary workspace for multiple files. Clean up the whole tree with os.RemoveAll when done.

package main
 
import (
    "fmt"
    "os"
    "path/filepath"
)
 
func main() {
    dir, err := os.MkdirTemp("", "workspace-")
    if err != nil {
        panic(err)
    }
    defer os.RemoveAll(dir) // clean up entire directory
 
    fmt.Println("temp dir:", dir)
 
    // Create files inside the temp directory
    p := filepath.Join(dir, "data.txt")
    err = os.WriteFile(p, []byte("hello"), 0600)
    if err != nil {
        panic(err)
    }
}

In tests, use t.TempDir() instead of os.MkdirTemp. It returns a unique directory that the test runner removes automatically when the test completes - no manual cleanup needed.

package mypackage_test
 
import (
    "os"
    "path/filepath"
    "testing"
)
 
func TestWriteConfig(t *testing.T) {
    dir := t.TempDir() // cleaned up automatically after the test
 
    cfg := filepath.Join(dir, "config.json")
    err := os.WriteFile(cfg, []byte(`{"debug":true}`), 0600)
    if err != nil {
        t.Fatal(err)
    }
 
    // test logic that reads from cfg...
}

In production

Always clean up temp files and directories, preferably with defer immediately after creation - a crash or early return without the defer running leaves them on disk. Temp files created by os.CreateTemp are not deleted on process exit. In tests, t.TempDir() is always preferable to manual os.MkdirTemp + defer os.RemoveAll - the test runner handles cleanup even if the test panics. When writing to a temp file and then renaming it over a real file (atomic write pattern), create the temp file in the same directory as the target so the rename stays on the same filesystem - cross-device renames fail with EXDEV. Set restrictive permissions (0600 for files, 0700 for directories) on temp paths that contain sensitive data; os.TempDir() is world-readable on Linux by default.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free