fatedier/frp · error

failed to create TUN device '%s': %w

Error message

failed to create TUN device '%s': %w

What it means

Linux implementation of vnet's openTun: wireguard/tun's CreateTUN failed to create the utun device, and this wraps the driver-level error. Creating a TUN device requires the kernel TUN/TAP module and privileges (CAP_NET_ADMIN), so on Linux this almost always means /dev/net/tun is missing/inaccessible or the process lacks permission.

Source

Thrown at pkg/vnet/tun_linux.go:43

	"github.com/vishvananda/netlink"
	"golang.zx2c4.com/wireguard/tun"
)

const (
	baseTunName = "utun"
	defaultMTU  = 1420
)

func openTun(_ context.Context, addr string) (tun.Device, error) {
	name, err := findNextTunName(baseTunName)
	if err != nil {
		name = getFallbackTunName(baseTunName, addr)
	}

	tunDevice, err := tun.CreateTUN(name, defaultMTU)
	if err != nil {
		return nil, fmt.Errorf("failed to create TUN device '%s': %w", name, err)
	}

	actualName, err := tunDevice.Name()
	if err != nil {
		return nil, err
	}

	ifn, err := net.InterfaceByName(actualName)
	if err != nil {
		return nil, err
	}

	link, err := netlink.LinkByName(actualName)
	if err != nil {
		return nil, err
	}

	ip, cidr, err := net.ParseCIDR(addr)

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Load the module on the host: `sudo modprobe tun` and ensure /dev/net/tun exists (mknod /dev/net/tun c 10 200)
  2. Run frpc with CAP_NET_ADMIN (root, or setcap/ambients); in Docker add --cap-add=NET_ADMIN --device /dev/net/tun (or --privileged)
  3. If the kernel truly lacks TUN support, disable vnet: set vnet.enabled = false in frpc.toml
  4. Check dmesg/audit for SELinux/AppArmor denials when the device node exists yet creation fails

Example fix

# before
docker run myfrpc  # vnet enabled -> failed to create TUN device 'utun0': operation not permitted

# after
docker run --cap-add=NET_ADMIN --device /dev/net/tun myfrpc
Defensive patterns

Strategy: validation

Validate before calling

// capability check before enabling vnet on linux
func tunAvailable() bool {
    f, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
    if err != nil { return false }
    f.Close()
    return os.Geteuid() == 0 || hasCapNetAdmin() // hasCapNetAdmin: parse /proc/self/status CapEff
}

Try / catch

if err := startVnet(); err != nil {
    if strings.Contains(err.Error(), "failed to create TUN device") {
        log.Printf("vnet needs /dev/net/tun + CAP_NET_ADMIN; disabling vnet: %v", err)
        cfg.Vnet.Enabled = false // degrade gracefully
    }
}

Prevention

When it happens

Trigger: frpc started with vnet.enabled = true in an environment where: /dev/net/tun does not exist (module not loaded / not passed into container), the process runs as non-root without CAP_NET_ADMIN, the container lacks --device /dev/net/tun / --privileged, or the tun kernel module is blacklisted.

Common situations: Running frpc in Docker/Kubernetes without privileged mode or without mounting /dev/net/tun; minimal VMs/WSL2 where the tun module is absent; systemd hardening (RestrictAddressFamilies, CapabilityBoundingSet) dropping AF_UNIX/CAP_NET_ADMIN; already-exhausted or locked-down device creation.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/c4f71f41b2e4fc2f. Report an issue: GitHub.