{"record":{"id":"cb1e1f60d951e886","repo":"tailscale/tailscale","slug":"unable-to-create-tuntap-device-file-w","errorCode":null,"errorMessage":"unable to create tuntap device file: %w","messagePattern":"unable to create tuntap device file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"cmd/containerboot/main.go","lineNumber":337,"sourceCode":"}\n\nfunc main() {\n\tif err := run(); err != nil && !errors.Is(err, context.Canceled) {\n\t\tlog.Fatal(err)\n\t}\n}\n\nfunc run() error {\n\tlog.SetPrefix(\"boot: \")\n\n\tcfg, err := configFromEnv()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid configuration: %w\", err)\n\t}\n\n\tif !cfg.UserspaceMode {\n\t\tif err := ensureTunFile(cfg.Root); err != nil {\n\t\t\treturn fmt.Errorf(\"unable to create tuntap device file: %w\", err)\n\t\t}\n\t\tif cfg.ProxyTargetIP != \"\" || cfg.ProxyTargetDNSName != \"\" || cfg.Routes != nil || cfg.TailnetTargetIP != \"\" || cfg.TailnetTargetFQDN != \"\" {\n\t\t\tif err := ensureIPForwarding(cfg.Root, cfg.ProxyTargetIP, cfg.TailnetTargetIP, cfg.TailnetTargetFQDN, cfg.Routes); err != nil {\n\t\t\t\tlog.Printf(\"Failed to enable IP forwarding: %v\", err)\n\t\t\t\tlog.Printf(\"To run tailscale as a proxy or router container, IP forwarding must be enabled.\")\n\t\t\t\tif cfg.InKubernetes {\n\t\t\t\t\treturn fmt.Errorf(\"you can either set the sysctls as a privileged initContainer, or run the tailscale container with privileged=true.\")\n\t\t\t\t} else {\n\t\t\t\t\treturn fmt.Errorf(\"you can fix this by running the container with privileged=true, or the equivalent in your container runtime that permits access to sysctls.\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Root context for the whole containerboot process, used to make sure\n\t// shutdown signals are promptly and cleanly handled.\n\tctx, cancel := contextWithExitSignalWatch()\n\tdefer cancel()","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/containerboot/main.go#L319-L355","documentation":"In kernel mode (TS_USERSPACE unset/false), containerboot ensures /dev/net/tun exists, creating /dev/net via MkdirAll and the device node via unix.Mknod(major 10, minor 200) if missing. Failure of either filesystem operation becomes this fatal error before tailscaled starts. It is purely a container-capability/filesystem problem, not a Kubernetes API one.","triggerScenarios":"unix.Mknod or os.MkdirAll failing when the container lacks CAP_MKNOD (unprivileged Pod), the root filesystem is read-only, a securityProfile (runAsNon-root, seccomp, AppArmor) blocks device creation, or /dev/net already exists but /dev is a ro mount.","commonSituations":"Custom ProxyClass or hand-written manifest removing privileged:true without adding CAP_MKNOD+CAP_NET_ADMIN; hardened nodes (SELinux denying mknod); running the image in docker/podman without --privileged and without a host /dev/net/tun bind-mount.","solutions":["Set TS_USERSPACE=true if TUN networking is not required (userspace mode needs no device)","Run the container privileged (operator default for proxies) or grant CAP_MKNOD and CAP_NET_ADMIN","Bind-mount the host device instead of creating it: devices: [{path: /dev/net/tun}] in the container spec","Verify in-container: ls -l /dev/net/tun after applying the fix"],"exampleFix":"# before\n# unprivileged pod, kernel mode -> mknod fails\nTS_USERSPACE unset\nsecurityContext: {runAsNonRoot: true}\n\n# after (option 1: userspace mode, no tun needed)\nenv: [{name: TS_USERSPACE, value: \"true\"}]\n\n# after (option 2: keep kernel mode, provide the device)\nsecurityContext:\n  capabilities: {add: [\"NET_ADMIN\", \"MKNOD\"]}\nvolumes: [{hostPath: {path: /dev/net/tun}, name: tun}]\nvolumeMounts: [{mountPath: /dev/net/tun, name: tun}]","handlingStrategy":"validation","validationCode":"// Skip device creation when it cannot succeed, choosing userspace mode up front\nprivileged := detectCapabilities() // e.g. check CAP_MKNOD in /proc/self/status CapEff\nif !privileged && def.Bool(os.Getenv(\"TS_USERSPACE\"), true) == false {\n\treturn errors.New(\"kernel mode requires CAP_MKNOD/NET_ADMIN or a mounted /dev/net/tun — set TS_USERSPACE=true or fix securityContext\")\n}","typeGuard":"func tunAvailable(root string) bool {\n\t_, err := os.Stat(filepath.Join(root, \"dev/net/tun\"))\n\treturn err == nil\n}","tryCatchPattern":"if err := ensureTunFile(cfg.Root); err != nil {\n\t// permanent for this securityContext — do not retry; either fix caps/mount or switch to userspace mode\n\treturn fmt.Errorf(\"unable to create tuntap device file: %w\", err)\n}","preventionTips":["Set TS_USERSPACE=true for any Pod that does not need kernel TUN (pure L7 proxies, sidecars)","For kernel mode: run privileged, or grant NET_ADMIN+MKNOD, or bind-mount the host's /dev/net/tun","Validate securityContext in CI by launching the image once and asserting /dev/net/tun is usable"],"tags":["tailscale","kubernetes","linux","tun","capabilities","container","go"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}