{"record":{"id":"221e5e1740e6f2d0","repo":"slackhq/nebula","slug":"dev-net-tun-doesn-t-exist-failed-to-mkdir-p-de","errorCode":null,"errorMessage":"/dev/net/tun doesn't exist, failed to mkdir -p /dev/net: %w","messagePattern":"/dev/net/tun doesn't exist, failed to mkdir -p /dev/net: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"overlay/tun_linux.go","lineNumber":94,"sourceCode":"\nfunc newTunFromFd(c *config.C, l *slog.Logger, deviceFd int, vpnNetworks []netip.Prefix) (*tun, error) {\n\t// We don't know what flags the caller opened this fd with and can't turn\n\t// on IFF_VNET_HDR after TUNSETIFF, so skip offload on inherited fds.\n\treturn newTunGeneric(c, l, deviceFd, false, 0, vpnNetworks, \"tun0\")\n}\n\n// openTunDev opens /dev/net/tun, creating the device node first if it's\n// missing (docker containers occasionally omit it).\nfunc openTunDev() (int, error) {\n\tfd, err := unix.Open(\"/dev/net/tun\", os.O_RDWR, 0)\n\tif err == nil {\n\t\treturn fd, nil\n\t}\n\tif !os.IsNotExist(err) {\n\t\treturn -1, err\n\t}\n\tif err = os.MkdirAll(\"/dev/net\", 0755); err != nil {\n\t\treturn -1, fmt.Errorf(\"/dev/net/tun doesn't exist, failed to mkdir -p /dev/net: %w\", err)\n\t}\n\tif err = unix.Mknod(\"/dev/net/tun\", unix.S_IFCHR|0600, int(unix.Mkdev(10, 200))); err != nil {\n\t\treturn -1, fmt.Errorf(\"failed to create /dev/net/tun: %w\", err)\n\t}\n\tfd, err = unix.Open(\"/dev/net/tun\", os.O_RDWR, 0)\n\tif err != nil {\n\t\treturn -1, fmt.Errorf(\"created /dev/net/tun, but still failed: %w\", err)\n\t}\n\treturn fd, nil\n}\n\n// tunSetIff runs TUNSETIFF with the given flags and returns the kernel-chosen device name on success.\nfunc tunSetIff(fd int, name string, flags uint16) (string, error) {\n\tvar req ifReq\n\treq.Flags = flags\n\tcopy(req.Name[:], name)\n\tif err := ioctl(uintptr(fd), uintptr(unix.TUNSETIFF), uintptr(unsafe.Pointer(&req))); err != nil {\n\t\treturn \"\", err","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_linux.go#L76-L112","documentation":"openTunDev (overlay/tun_linux.go:94) opens /dev/net/tun; if it does not exist, it creates the /dev/net directory with MkdirAll. This error wraps a failure of that mkdir, so the TUN character device cannot be provisioned.","triggerScenarios":"os.MkdirAll(\"/dev/net\", 0755) fails after /dev/net/tun was found missing: read-only /dev (typical in containers), or EACCES/EPERM without root privileges.","commonSituations":"Running nebula in a Docker/Kubernetes container without --privileged and with a read-only /dev; minimal chroot/initramfs environments lacking /dev/net; SELinux/AppArmor policies denying writes under /dev.","solutions":["Run the container with --privileged (or at least --device /dev/net/tun and a writable /dev).","Ensure the process runs as root with CAP_MKNOD and CAP_NET_ADMIN.","Pre-create /dev/net/tun on the host or in the image (mknod /dev/net/tun c 10 200) so the mkdir path is never taken.","Check LSM policies (SELinux/AppArmor) that may block directory creation under /dev."],"exampleFix":"// before\n$ docker run nebula-image\n// after\n$ docker run --device /dev/net/tun --cap-add NET_ADMIN nebula-image","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(\"/dev/net/tun\"); os.IsNotExist(err) {\n\tif st, serr := os.Stat(\"/dev\"); serr != nil || st.Mode()&os.ModePerm == 0 || isReadOnlyFs(\"/dev\") {\n\t\treturn errors.New(\"/dev/net/tun missing and /dev not writable; use --device /dev/net/tun or privileged mode\")\n\t}\n}\nif os.Geteuid() != 0 {\n\treturn errors.New(\"provisioning /dev/net/tun requires root\")\n}","typeGuard":null,"tryCatchPattern":"fd, err := openTunDev()\nif err != nil && strings.Contains(err.Error(), \"failed to mkdir -p /dev/net\") {\n\t// fall back to requesting the device from the container runtime / operator\n}","preventionTips":["Run containers with --device /dev/net/tun or --privileged.","Bake the device node into images where /dev is writable at build time.","Ensure the host kernel has the tun module loaded so the device exists.","Check SELinux/AppArmor policies before deploying to hardened hosts."],"tags":["linux","tun","devfs","container","permissions"],"backgroundTag":"missing-dev-net-tun","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}