{"record":{"id":"f9ea93529d8f7fcf","repo":"juanfont/headscale","slug":"setting-up-unix-socket-w","errorCode":null,"errorMessage":"setting up unix socket: %w","messagePattern":"setting up unix socket: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"hscontrol/app.go","lineNumber":626,"sourceCode":"\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\t//\n\t//\n\t// Set up LOCAL listeners\n\t//\n\n\terr = h.ensureUnixSocketIsAbsent()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"removing old socket file: %w\", err)\n\t}\n\n\tsocketDir := filepath.Dir(h.cfg.UnixSocket)\n\n\terr = util.EnsureDir(socketDir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"setting up unix socket: %w\", err)\n\t}\n\n\tsocketListener, err := new(net.ListenConfig).Listen(context.Background(), \"unix\", h.cfg.UnixSocket)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"setting up socket: %w\", err)\n\t}\n\n\t// Change socket permissions\n\tif err := os.Chmod(h.cfg.UnixSocket, h.cfg.UnixSocketPermission); err != nil { //nolint:noinlineerr\n\t\treturn fmt.Errorf(\"changing socket permission: %w\", err)\n\t}\n\n\t// The Huma v1 API mux matches full /api/v1/... paths and is shared by\n\t// the local unix socket (served without authentication, local trust)\n\t// and the remote TCP router (served behind the API-key middleware).\n\thumaMux, _ := apiv1.Handler(apiv1.Backend{\n\t\tState:  h.state,\n\t\tChange: h.Change,","sourceCodeStart":608,"sourceCodeEnd":644,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/app.go#L608-L644","documentation":"Thrown during startup when util.EnsureDir fails to create the parent directory of the configured unix_socket path (socketDir := filepath.Dir(h.cfg.UnixSocket) in hscontrol/app.go:623). EnsureDir does a MkdirAll on that directory; failure means the directory could not be created or already exists with wrong permissions/ownership. Startup aborts before listening.","triggerScenarios":"unix_socket is set to a path like /run/headscale/headscale.sock but /run/headscale cannot be created because /run is root-owned and headscale runs unprivileged (EACCES); the target path component exists as a regular file (ENOTDIR); a read-only filesystem (EROFS), commonly containers with a read-only rootfs and no writable volume for the socket.","commonSituations":"Running headscale in Docker/Kubernetes without a writable volume or tmpfs at the socket path; running as the headscale system user without RuntimeDirectory= set in the systemd unit (so /run/headscale never exists); SELinux denying writes to /run; moving config to /etc but leaving unix_socket pointing somewhere unwritable.","solutions":["Pre-create the socket directory with correct ownership: install -d -o headscale -g headscale /run/headscale (or add RuntimeDirectory=headscale to the systemd unit).","If in a container, mount a writable tmpfs/volume at the socket directory: docker run ... --tmpfs /var/run/headscale.","Check for path-component conflicts: a regular file sitting where a directory is needed (ls -ld each component) and remove or relocate it.","If SELinux is enforcing, set the correct context (restorecon -v on the directory) or adjust the policy for the headscale process."],"exampleFix":"# before: systemd unit without runtime dir, /run/headscale missing -> EACCES\n[Service]\nUser=headscale\n\n# after\n[Service]\nUser=headscale\nRuntimeDirectory=headscale\nRuntimeDirectoryMode=0755","handlingStrategy":"validation","validationCode":"// Pre-flight: ensure the socket dir is creatable/writable.\nfunc ensureSocketDir(socketPath string) error {\n    dir := filepath.Dir(socketPath)\n    if err := os.MkdirAll(dir, 0o755); err != nil {\n        return fmt.Errorf(\"socket dir not writable: %w\", err)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := h.Serve(); err != nil && strings.Contains(err.Error(), \"setting up unix socket\") {\n    log.Fatalf(\"socket directory problem, check ownership of %s: %v\", filepath.Dir(cfg.UnixSocket), err)\n}","preventionTips":["Set RuntimeDirectory=headscale in the systemd unit so /run/headscale exists with right owner.","Mount a writable volume/tmpfs at the socket dir in containers.","Verify directory ownership in config-management playbooks before starting the service."],"tags":["unix-socket","filesystem","startup","permissions","containers"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}