slackhq/nebula · error

must be using user device

Error message

must be using user device

What it means

service.New wraps a nebula Control into the service (device-tunnel) API. It requires the control's device be an *overlay.UserDevice (tun backed by a user-space device handle); any other device type fails with this error before Start is called. The service layer needs the user device to expose file-descriptor based tunneling to the OS.

Source

Thrown at service/service.go:50

const nicID = 1

type Service struct {
	eg      *errgroup.Group
	control *nebula.Control
	ipstack *stack.Stack

	mu struct {
		sync.Mutex

		listeners map[uint16]*tcpListener
	}
}

func New(control *nebula.Control) (_ *Service, reterr error) {
	// Check this before Start so a failure doesn't leave a running nebula
	device, ok := control.Device().(*overlay.UserDevice)
	if !ok {
		return nil, errors.New("must be using user device")
	}

	err := control.Start()
	if err != nil {
		return nil, err
	}

	// Anything that fails after a successful Start must tear nebula back down
	defer func() {
		if reterr != nil {
			control.Stop()
		}
	}()

	ctx := control.Context()
	eg, ctx := errgroup.WithContext(ctx)
	s := Service{
		eg:      eg,

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Start nebula with the user device enabled so control.Device() returns *overlay.UserDevice (service mode requires it).
  2. Check the config/CLI flags that select device type and switch from kernel tun to user device.
  3. If you don't need service/device-tunnel functionality, use the plain nebula Control API instead of service.New.

Example fix

// before (kernel tun)
control, _ := nebula.Main(config, false, "", nil, nil)
s, err := service.New(control) // panics into error: must be using user device
// after: launch with user device support, e.g.
// nebula -service -config config.yaml  (which configures overlay.UserDevice)
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := control.Device().(*overlay.UserDevice); !ok {
    return errors.New("service.New requires a user device; enable user-space tun")
}

Type guard

func isUserDevice(control *nebula.Control) bool {
    _, ok := control.Device().(*overlay.UserDevice)
    return ok
}

Try / catch

svc, err := service.New(control)
if err != nil {
    if err.Error() == "must be using user device" {
        return fmt.Errorf("service mode requires the user device; check your device/tun configuration")
    }
    return err
}

Prevention

When it happens

Trigger: Calling service.New(control) (from doService, run, or newSimpleService) when control.Device() type-asserts to anything other than *overlay.UserDevice — e.g. the node was started with a regular kernel tun device.

Common situations: Running nebula in an environment without the user-device tun option enabled; config using a normal tun interface where service mode expects `-usb`/user device support; embedding the service API against a control built with default tun settings.

Related errors


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