tailscale/tailscale · error

unable to create /userdata/init.d

Error message

unable to create /userdata/init.d

What it means

os.MkdirAll('/userdata/init.d', 0755) failed while `tailscale configure jetkvm` was preparing the directory for its S22tailscale init script. The returned error is created with errors.New and does NOT wrap the underlying os error, so the real cause (permissions, read-only filesystem, path exists as a file, disk full) is hidden from the message. This discard-the-cause pattern makes diagnosis harder than it should be.

Source

Thrown at cmd/tailscale/cli/configure-jetkvm.go:52

		LongHelp: strings.TrimSpace(`
This command configures the JetKVM host to run tailscaled at boot.
`),
		FlagSet: (func() *flag.FlagSet {
			fs := newFlagSet("jetkvm")
			return fs
		})(),
	}
}

func runConfigureJetKVM(ctx context.Context, args []string) error {
	if len(args) > 0 {
		return errors.New("unknown arguments")
	}
	if runtime.GOOS != "linux" || distro.Get() != distro.JetKVM {
		return errors.New("only implemented on JetKVM")
	}
	if err := os.MkdirAll("/userdata/init.d", 0755); err != nil {
		return errors.New("unable to create /userdata/init.d")
	}
	err := os.WriteFile("/userdata/init.d/S22tailscale", bytes.TrimLeft([]byte(`
#!/bin/sh
# /userdata/init.d/S22tailscale
# Start/stop tailscaled

case "$1" in
  start)
    /userdata/tailscale/tailscaled > /dev/null 2>&1 &
    ;;
  stop)
    killall tailscaled
    ;;
  *)
    echo "Usage: $0 {start|stop}"
    exit 1
    ;;
esac

View on GitHub (pinned to cfe32b8be6)

Solutions

  1. Re-run as root: `sudo tailscale configure jetkvm`
  2. Check the path manually: `ls -la /userdata/`, `mount | grep userdata`, `findmnt -no OPTIONS /userdata` (look for ro), and `df -h /userdata`
  3. If a regular file blocks the directory, remove or rename it, then retry
  4. Reproduce with `mkdir -p /userdata/init.d` to surface the real errno the CLI swallowed

Example fix

# before
$ tailscale configure jetkvm
error: unable to create /userdata/init.d

# after (diagnose, then retry)
$ sudo mkdir -p /userdata/init.d && sudo tailscale configure jetkvm
Defensive patterns

Strategy: validation

Validate before calling

# Preconditions for writing the JetKVM init script
[ "$(id -u)" = "0" ] || { echo 'run as root'; exit 1; }
mkdir -p /userdata/init.d 2>/dev/null || { echo 'cannot create /userdata/init.d'; mount | grep userdata; df -h /userdata; exit 1; }
findmnt -no OPTIONS /userdata | grep -qw ro && { echo '/userdata is read-only'; exit 1; }

Try / catch

err := os.MkdirAll("/userdata/init.d", 0o755)
if err != nil {
    return fmt.Errorf("creating /userdata/init.d: %w", err) // keep the cause, unlike the CLI
}

Prevention

When it happens

Trigger: Running the command as a non-root user on a JetKVM device; /userdata being mounted read-only or not mounted at all; an existing regular file named /userdata/init.d; a full userdata partition.

Common situations: Forgetting sudo on the JetKVM shell; the userdata partition not being mounted after a firmware reflash; leftover file occupying the init.d path from a previous manual install.

Related errors


AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15). Data as JSON: /api/errors/e8b5b76fabb7169a. Report an issue: GitHub.