{"record":{"id":"36e1941143502705","repo":"tailscale/tailscale","slug":"error-waiting-for-tailscaled-socket-w","errorCode":null,"errorMessage":"error waiting for tailscaled socket: %w","messagePattern":"error waiting for tailscaled socket: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/containerboot/tailscaled.go","lineNumber":55,"sourceCode":"\t\tcmd.Env = append(os.Environ(), \"TS_CERT_SHARE_MODE=\"+cfg.CertShareMode)\n\t}\n\tlog.Printf(\"Starting tailscaled\")\n\tif err := cmd.Start(); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"starting tailscaled failed: %w\", err)\n\t}\n\n\t// Wait for the socket file to appear, otherwise API ops will racily fail.\n\tlog.Printf(\"Waiting for tailscaled socket at %s\", cfg.Socket)\n\tfor {\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil, nil, errors.New(\"timed out waiting for tailscaled socket\")\n\t\t}\n\t\t_, err := os.Stat(cfg.Socket)\n\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"error waiting for tailscaled socket: %w\", err)\n\t\t}\n\t\tbreak\n\t}\n\n\ttsClient := &local.Client{\n\t\tSocket:        cfg.Socket,\n\t\tUseSocketOnly: true,\n\t}\n\n\treturn tsClient, cmd.Process, nil\n}\n\n// tailscaledArgs uses cfg to construct the argv for tailscaled.\nfunc tailscaledArgs(cfg *settings) []string {\n\targs := []string{\"--socket=\" + cfg.Socket}\n\tswitch {\n\tcase cfg.KubeSecret != \"\":\n\t\targs = append(args, \"--state=kube:\"+cfg.KubeSecret)","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/containerboot/tailscaled.go#L37-L73","documentation":"tailscaled.go:55 is hit inside the wait loop for the tailscaled socket: os.Stat(cfg.Socket) returned an error that is not fs.ErrNotExist (the loop tolerates a missing file and keeps polling). Typically EACCES/permission denied on the socket path or its parent directory, or the path exists but is not a regular unix socket file.","triggerScenarios":"TS_SOCKET (or the default /var/run/tailscale/tailscaled.sock) lives in a directory the container cannot traverse due to read-only mounts, wrong fsGroup, or SELinux; the path points at a directory; the socket was created by a different UID.","commonSituations":"Non-root securityContext with restrictive volumeMounts on /var/run/tailscale; mounting a hostPath with root-owned perms; overriding TS_SOCKET to a path under a read-only volume.","solutions":["Check the stat error in the log (it is wrapped with %w) to confirm permission vs other causes","Ensure the socket directory is writable by the container user (fsGroup, runAsUser, or writable emptyDir/hostPath)","Verify TS_SOCKET points at a socket file path, not a directory","Run with the stock image defaults, which provision /var/run/tailscale correctly"],"exampleFix":"# before\nsecurityContext:\n  runAsNonRoot: true\n# volume mounted read-only at /var/run/tailscale\n# after\nsecurityContext:\n  runAsNonRoot: true\n  fsGroup: 1000\nvolumes:\n  - name: run\n    emptyDir: {} # writable socket dir","handlingStrategy":"validation","validationCode":"// Preflight: the socket path must be stat-able by this process (parent dir traversable).\ndir := filepath.Dir(socketPath)\nif info, err := os.Stat(dir); err != nil || !info.IsDir() {\n    log.Fatalf(\"socket dir %s unusable: %v\", dir, err)\n}\nif _, err := os.Stat(socketPath); err != nil && !errors.Is(err, fs.ErrNotExist) {\n    log.Fatalf(\"socket path %s not checkable (permissions?): %v\", socketPath, err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Mount an writable emptyDir at /var/run/tailscale for non-root proxies","Set fsGroup matching the socket dir ownership when runAsNonRoot","Never point TS_SOCKET at a read-only volume or a directory path"],"tags":["containerboot","unix-socket","permissions","kubernetes","startup","go"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}