juanfont/headscale · critical
setting up socket: %w
Error message
setting up socket: %w
What it means
Returned when net.ListenConfig.Listen("unix", h.cfg.UnixSocket) fails at hscontrol/app.go:631, i.e. the operating system refused to bind a Unix domain socket at the configured path. Despite the generic wording, this is specifically the bind of the local admin/gRPC socket, not the TCP listener. The wrapped error is the raw errno from bind(2).
Source
Thrown at hscontrol/app.go:631
//
// Set up LOCAL listeners
//
err = h.ensureUnixSocketIsAbsent()
if err != nil {
return fmt.Errorf("removing old socket file: %w", err)
}
socketDir := filepath.Dir(h.cfg.UnixSocket)
err = util.EnsureDir(socketDir)
if err != nil {
return fmt.Errorf("setting up unix socket: %w", err)
}
socketListener, err := new(net.ListenConfig).Listen(context.Background(), "unix", h.cfg.UnixSocket)
if err != nil {
return fmt.Errorf("setting up socket: %w", err)
}
// Change socket permissions
if err := os.Chmod(h.cfg.UnixSocket, h.cfg.UnixSocketPermission); err != nil { //nolint:noinlineerr
return fmt.Errorf("changing socket permission: %w", err)
}
// The Huma v1 API mux matches full /api/v1/... paths and is shared by
// the local unix socket (served without authentication, local trust)
// and the remote TCP router (served behind the API-key middleware).
humaMux, _ := apiv1.Handler(apiv1.Backend{
State: h.state,
Change: h.Change,
Cfg: h.cfg,
})
// The Headscale v2 API. Served behind Basic/Bearer auth on the remote
// listener, and over the local unix socket (local trust) so the CLI canView on GitHub (pinned to 565fd254d0)
Solutions
- Re-run: in most race/stale-file cases the next start succeeds because ensureUnixSocketIsAbsent removes the offender; if not, manually rm the socket path.
- Shorten the socket path under 108 bytes total (e.g. /var/run/headscale/headscale.sock instead of deeply nested tmp dirs).
- Ensure the runtime user has write access to the socket directory (see also the 'setting up unix socket' error for the directory itself).
- Move the socket off network/unsupported filesystems onto local disk or a tmpfs.
Example fix
# before unix_socket: /mnt/shared/nfs/headscale/headscale.sock # NFS: bind fails # after unix_socket: /var/run/headscale/headscale.sock
Defensive patterns
Strategy: validation
Validate before calling
// Keep the unix socket path within the kernel sun_path limit.
func socketPathValid(p string) bool { return len(p) < 108 }
// Fail fast if the path is on a filesystem that cannot host sockets.
func probeUnixSocketFS(dir string) error {
probe := filepath.Join(dir, ".probe.sock")
l, err := net.Listen("unix", probe)
if err != nil { return err }
l.Close()
return os.Remove(probe)
} Try / catch
if err := h.Serve(); err != nil {
if errors.Is(err, syscall.EADDRINUSE) || strings.Contains(err.Error(), "setting up socket") {
// another instance or unsupported fs: stop instead of retry-looping
}
} Prevention
- Ensure a single headscale process per socket path (systemd is the natural mutex).
- Use short, local-filesystem socket paths.
- Avoid NFS/SMB bind mounts for the socket directory.
When it happens
Trigger: The path was recreated between the removal step and bind (race with another process); path length exceeds the ~108-byte sun_path limit (EINVAL/ENAMETOOLONG); the parent directory is not writable (EACCES); the filesystem does not support Unix sockets, e.g. unix_socket on an NFS/SMB/vBoxsf mount (EINVAL/ENODEV); a file with that name appeared and bind fails with EADDRINUSE.
Common situations: Two headscale instances starting concurrently with the same socket; unix_socket set to a long path (deep tmpdir in CI or macOS runner paths); socket placed on a network mount or a Windows bind-mount in Docker; a non-socket file created at the path by a script after cleanup.
Related errors
- removing old socket file: %w
- setting up unix socket: %w
- changing socket permission: %w
- creating directory failed with permission error
- reading or creating Noise protocol private key: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/3204864f442abd34.
Report an issue: GitHub.