tailscale/tailscale · critical
unable to create tuntap device file: %w
Error message
unable to create tuntap device file: %w
What it means
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.
Source
Thrown at cmd/containerboot/main.go:337
}
func main() {
if err := run(); err != nil && !errors.Is(err, context.Canceled) {
log.Fatal(err)
}
}
func run() error {
log.SetPrefix("boot: ")
cfg, err := configFromEnv()
if err != nil {
return fmt.Errorf("invalid configuration: %w", err)
}
if !cfg.UserspaceMode {
if err := ensureTunFile(cfg.Root); err != nil {
return fmt.Errorf("unable to create tuntap device file: %w", err)
}
if cfg.ProxyTargetIP != "" || cfg.ProxyTargetDNSName != "" || cfg.Routes != nil || cfg.TailnetTargetIP != "" || cfg.TailnetTargetFQDN != "" {
if err := ensureIPForwarding(cfg.Root, cfg.ProxyTargetIP, cfg.TailnetTargetIP, cfg.TailnetTargetFQDN, cfg.Routes); err != nil {
log.Printf("Failed to enable IP forwarding: %v", err)
log.Printf("To run tailscale as a proxy or router container, IP forwarding must be enabled.")
if cfg.InKubernetes {
return fmt.Errorf("you can either set the sysctls as a privileged initContainer, or run the tailscale container with privileged=true.")
} else {
return 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.")
}
}
}
}
// Root context for the whole containerboot process, used to make sure
// shutdown signals are promptly and cleanly handled.
ctx, cancel := contextWithExitSignalWatch()
defer cancel()View on GitHub (pinned to cfe32b8be6)
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
Example fix
# before
# unprivileged pod, kernel mode -> mknod fails
TS_USERSPACE unset
securityContext: {runAsNonRoot: true}
# after (option 1: userspace mode, no tun needed)
env: [{name: TS_USERSPACE, value: "true"}]
# after (option 2: keep kernel mode, provide the device)
securityContext:
capabilities: {add: ["NET_ADMIN", "MKNOD"]}
volumes: [{hostPath: {path: /dev/net/tun}, name: tun}]
volumeMounts: [{mountPath: /dev/net/tun, name: tun}] Defensive patterns
Strategy: validation
Validate before calling
// Skip device creation when it cannot succeed, choosing userspace mode up front
privileged := detectCapabilities() // e.g. check CAP_MKNOD in /proc/self/status CapEff
if !privileged && def.Bool(os.Getenv("TS_USERSPACE"), true) == false {
return errors.New("kernel mode requires CAP_MKNOD/NET_ADMIN or a mounted /dev/net/tun — set TS_USERSPACE=true or fix securityContext")
} Type guard
func tunAvailable(root string) bool {
_, err := os.Stat(filepath.Join(root, "dev/net/tun"))
return err == nil
} Try / catch
if err := ensureTunFile(cfg.Root); err != nil {
// permanent for this securityContext — do not retry; either fix caps/mount or switch to userspace mode
return fmt.Errorf("unable to create tuntap device file: %w", err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- error deleting ingress rules: %w
- error adding ingress rules: %w
- error adding ingress rule for %s: %w
- error clamping MSS to PMTU: %w
- error deleting ingress rule for %s: %w
AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15).
Data as JSON: /api/errors/cb1e1f60d951e886.
Report an issue: GitHub.