slackhq/nebula · error

failed to bring the tun device up

Error message

failed to bring the tun device up: %s

What it means

Raised in tun.Activate when the SIOCSIFFLAGS ioctl that sets IFF_UP on the tun interface fails. The device exists and its flags were read successfully, but the kernel refused to bring the interface administratively up.

Solutions

  1. Run with sufficient privileges (root or CAP_NET_ADMIN) so interface flags can be changed.
  2. Check the %s errno: EPERM = permissions, ENODEV = interface gone.
  3. Ensure the device isn't being torn down concurrently (unmanaged in NetworkManager/systemd-networkd).
  4. Manually verify with `ip link set <dev> up` to confirm it's a privilege vs device issue.

Example fix

// before
$ ./nebula -config config.yml  # EPERM
// after
$ sudo setcap cap_net_admin=ep ./nebula
$ ./nebula -config config.yml
Defensive patterns

Strategy: validation

Validate before calling

// require privileges before attempting to bring the interface up
if os.Geteuid() != 0 && !hasCapNetAdmin() {
    return errors.New("bringing tun up requires root or CAP_NET_ADMIN")
}

Try / catch

if err := t.Activate(netstack); err != nil {
    if strings.Contains(err.Error(), "failed to bring the tun device up") {
        if errors.Is(err, os.ErrPermission) || strings.Contains(err.Error(), "operation not permitted") {
            log.Error("grant CAP_NET_ADMIN: setcap cap_net_admin=ep <binary>")
        }
    }
}

Prevention

When it happens

Trigger: Activate() calls ioctl(t.ioctlFd, SIOCSIFFLAGS, ...) with Flags|IFF_UP and the kernel returns an error — typically EPERM from missing privileges or ENODEV if the link vanished.

Common situations: Running nebula without root/CAP_NET_ADMIN; interface removed concurrently by a network manager; containers where SIOCSIFFLAGS on the host-owned device is denied.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/dfb26e48fe7c2092. Report an issue: GitHub.

Appendix: source

Thrown at overlay/tun_linux.go:490

	ifrq := ifreqQLEN{Name: devName, Value: int32(t.TXQueueLen)}
	if err = ioctl(t.ioctlFd, unix.SIOCSIFTXQLEN, uintptr(unsafe.Pointer(&ifrq))); err != nil {
		// If we can't set the queue length nebula will still work but it may lead to packet loss
		t.l.Error("Failed to set tun tx queue length", "error", err)
	}

	const modeNone = 1
	if err = netlink.LinkSetIP6AddrGenMode(link, modeNone); err != nil {
		t.l.Warn("Failed to disable link local address generation", "error", err)
	}

	if err = t.addIPs(link); err != nil {
		return err
	}

	// Bring up the interface
	ifrf.Flags = ifrf.Flags | unix.IFF_UP
	if err = ioctl(t.ioctlFd, unix.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifrf))); err != nil {
		return fmt.Errorf("failed to bring the tun device up: %s", err)
	}

	//set route MTU
	for i := range t.vpnNetworks {
		if err = t.setDefaultRoute(t.vpnNetworks[i]); err != nil {
			return fmt.Errorf("failed to set default route MTU: %w", err)
		}
	}

	// Set the routes
	if err = t.addRoutes(false); err != nil {
		return err
	}

	// Run the interface
	ifrf.Flags = ifrf.Flags | unix.IFF_UP | unix.IFF_RUNNING
	if err = ioctl(t.ioctlFd, unix.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifrf))); err != nil {
		return fmt.Errorf("failed to run tun device: %s", err)

View on GitHub (pinned to dd8f660c0a)