You golang create nested directory trees either level by level with os.Mkdir, or in one step with os.MkdirAll, which creates parent components as needed and succeeds if the path already exists. Combine with filepath.Join for portable paths. Shell-style mkdir -p is available through os/exec when that is clearer for your deployment.
Tested with Go 1.24 on Linux.
os.Mkdir one level at a time
Each call creates a single directory; the parent must exist, and repeating the program fails if the directory is already there unless you check first.
package main
import (
"fmt"
"os"
)
func main() {
myDir := "/tmp/glc-topdir"
if err := os.Mkdir(myDir, 0o755); err != nil && !os.IsExist(err) {
panic(err)
}
if err := os.Mkdir(myDir+"/subdir1", 0o755); err != nil && !os.IsExist(err) {
panic(err)
}
if err := os.Mkdir(myDir+"/subdir2", 0o755); err != nil && !os.IsExist(err) {
panic(err)
}
fmt.Println("ok")
}Use os.IsExist or errors.Is(err, fs.ErrExist) when you intentionally allow reruns.
os.MkdirAll (recommended)
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
p := filepath.Join("/tmp", "glc-mkdirall", "a", "b", "c")
if err := os.MkdirAll(p, 0o755); err != nil {
panic(err)
}
fi, err := os.Stat(p)
if err != nil {
panic(err)
}
fmt.Println(fi.IsDir())
}You should see true.
Helper that skips if the leaf already exists
package main
import (
"fmt"
"os"
"path/filepath"
)
func ensureDir(path string) error {
_, err := os.Stat(path)
if err == nil {
fmt.Println("already exists:", path)
return nil
}
if !os.IsNotExist(err) {
return err
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
return os.Mkdir(path, 0o755)
}
func main() {
path := filepath.Join("/tmp", "glc-ensure", "subdir1", "subdir2")
if err := ensureDir(path); err != nil {
panic(err)
}
fmt.Println("created or skipped:", path)
}You should see a created or skipped message and the final path printed.
Shell: mkdir -p via os/exec
package main
import (
"fmt"
"os/exec"
)
func main() {
path := "/tmp/glc-exec/a/b/c"
cmd := exec.Command("mkdir", "-p", path)
if err := cmd.Run(); err != nil {
panic(err)
}
fmt.Println("ok")
}Prefer os.MkdirAll when you do not need shell features; use exec when mirroring scripts or invoking platform tools (exec in Go).
Summary
For golang create nested directory workflows, os.MkdirAll with filepath.Join is the standard library sweet spot: idempotent for existing paths, one call for the whole tree, and no shell dependency. Use layered Mkdir plus Stat only when you need stricter control, and exec.Command("mkdir","-p",path) when shell parity matters.

