k3s-io/k3s · error

failed to create path for symlink: %v

Error message

failed to create path for symlink: %v

What it means

Before creating the kubeconfig symlink, k3s ensures its parent directory exists with os.MkdirAll (typically /etc/rancher/k3s). Failure of that mkdir - permission denied, path component is a file, read-only or immutable filesystem - is returned as this error during server startup.

Source

Thrown at pkg/server/server.go:530

	envList := []string{}
	envList = append(envList, strings.FieldsFunc(os.Getenv("NO_PROXY"), splitter)...)
	envList = append(envList, strings.FieldsFunc(os.Getenv("no_proxy"), splitter)...)
	envList = append(envList,
		".svc",
		"."+config.ClusterDomain,
		util.JoinIPNets(config.ClusterIPRanges),
		util.JoinIPNets(config.ServiceIPRanges),
	)
	os.Unsetenv("no_proxy")
	return os.Setenv("NO_PROXY", strings.Join(envList, ","))
}

func writeConfigSymlink(kubeconfig, kubeconfigSymlink string) error {
	if err := os.Remove(kubeconfigSymlink); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("failed to remove %s file: %v", kubeconfigSymlink, err)
	}
	if err := os.MkdirAll(filepath.Dir(kubeconfigSymlink), 0755); err != nil {
		return fmt.Errorf("failed to create path for symlink: %v", err)
	}
	if err := os.Symlink(kubeconfig, kubeconfigSymlink); err != nil {
		return fmt.Errorf("failed to create symlink: %v", err)
	}
	return nil
}

func isSymlink(config string) bool {
	if fi, err := os.Lstat(config); err == nil && (fi.Mode()&os.ModeSymlink == os.ModeSymlink) {
		return true
	}
	return false
}

func setNodeLabelsAndAnnotations(ctx context.Context, nodes v1.NodeClient, config *Config) error {
	if config.DisableAgent || config.ControlConfig.DisableAPIServer {
		return nil
	}

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Check the path shape: ls -ld /etc/rancher /etc/rancher/k3s - remove any regular file occupying a needed directory component.
  2. Run k3s as root (or the systemd unit) so it can create /etc/rancher/k3s, or pre-create the directory owned by the runtime user.
  3. In containers, ensure /etc/rancher is a writable volume/mount.
  4. Address any filesystem-level cause (read-only mount, SELinux denial) shown by the wrapped %v error.

Example fix

# before: /etc/rancher is a file
ls -ld /etc/rancher  # '-rw-r--r-- a file'

# after
sudo rm /etc/rancher && sudo mkdir -p /etc/rancher/k3s && sudo systemctl restart k3s
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the symlink parent dir is a writable directory before start
if fi, err := os.Stat(dir); err == nil && !fi.IsDir() {
    log.Fatalf("%s exists but is not a directory", dir)
} else if err := os.MkdirAll(dir, 0755); err != nil {
    log.Fatalf("cannot create %s: %v", dir, err)
}

Prevention

When it happens

Trigger: Server start where MkdirAll(/etc/rancher/k3s) fails: running k3s as a non-root user without write access to /etc, /etc/rancher existing as a regular file, a read-only root filesystem (immutable appliances, containers without writable /etc), or SELinux denial.

Common situations: Running the k3s binary manually as an unprivileged user instead of via the systemd unit; containerized deployments missing a writable /etc mount; a file accidentally created at /etc/rancher; disk or filesystem errors (I/O errors surface here too).

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/1a453a300d1531b4. Report an issue: GitHub.