juanfont/headscale · error · ErrDirectoryPermission
creating directory failed with permission error: %s
Error message
creating directory failed with permission error: %s
What it means
Returned by util.EnsureDir when os.MkdirAll fails with a permission error while creating a directory (wrapped with ErrDirectoryPermission and the path). Headscale creates runtime directories (e.g. certificate/state dirs) at startup and fails fast when the filesystem denies writes.
Source
Thrown at hscontrol/util/file.go:56
}
func GetFileMode(key string) fs.FileMode {
modeStr := viper.GetString(key)
mode, err := strconv.ParseUint(modeStr, Base8, BitSize64)
if err != nil {
return PermissionFallback
}
return fs.FileMode(mode) //nolint:gosec // file mode is bounded by ParseUint
}
func EnsureDir(dir string) error {
if _, err := os.Stat(dir); os.IsNotExist(err) { //nolint:noinlineerr
err := os.MkdirAll(dir, PermissionFallback)
if err != nil {
if errors.Is(err, os.ErrPermission) {
return fmt.Errorf("%w: %s", ErrDirectoryPermission, dir)
}
return fmt.Errorf("creating directory %s: %w", dir, err)
}
}
return nil
}
View on GitHub (pinned to 565fd254d0)
Solutions
- chown the directory tree to the user running headscale: sudo chown -R headscale:headscale /var/lib/headscale
- Fix container volume permissions (match PUID/PGID or use a named volume)
- Verify the filesystem is writable and not covered by SELinux/AppArmor policy
Example fix
# before sudo mkdir -p /var/lib/headscale && ls -ld /var/lib/headscale # owned by root # after sudo mkdir -p /var/lib/headscale && sudo chown -R headscale:headscale /var/lib/headscale
Defensive patterns
Strategy: validation
Validate before calling
import "os"
func canCreateDir(dir string) error {
parent := filepath.Dir(dir)
if _, err := os.Stat(parent); err != nil {
return fmt.Errorf("parent %s missing: %w", parent, err)
}
if err := unix.Access(parent, unix.W_OK); err != nil { // or attempt a temp file
return fmt.Errorf("no write permission on %s", parent)
}
return nil
} Try / catch
if err := util.EnsureDir(path); err != nil {
if errors.Is(err, util.ErrDirectoryPermission) {
// prompt to chown / fix volume, do not retry blindly
}
return err
} Prevention
- Pre-create runtime directories in your unit spec / container image with correct ownership
- Run headscale under a dedicated user and chown its data dirs at install time
- Smoke-test directory writability in entrypoint scripts before exec'ing the server
When it happens
Trigger: Calling EnsureDir on a path under a directory owned by root or another user while headscale runs unprivileged; read-only filesystems; containers where the volume is mounted with restrictive ownership.
Common situations: Running headscale as a non-root user for the first time against /var/lib/headscale created by root; Docker bind-mounts with wrong uid/gid; SELinux/AppArmor denials surfacing as EACCES.
Related errors
- creating directory failed with permission error
- reading or creating Noise protocol private key: %w
- reading or creating DERP server private key: %w
- removing old socket file: %w
- setting up unix socket: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/cf3fdd39a7d31827.
Report an issue: GitHub.