k3s-io/k3s · critical

pids cgroup controller not found

Error message

pids cgroup controller not found

What it means

Before building kubelet args, cgroups.CheckCgs() scans /sys/fs/cgroup for available controllers; the 'pids' controller is mandatory (kubelet requires it for pod limits), so its absence is fatal. The 'cpu' controller absence is only a warning (CPU quotas disabled), contrasted inline.

Source

Thrown at pkg/daemons/agent/agent_linux.go:131

	// If the embedded CCM is disabled, don't assume that dual-stack node IPs are safe.
	// When using an external CCM, the user wants dual-stack node IPs, they will need to set the node-ip kubelet arg directly.
	// This should be fine since most cloud providers have their own way of finding node IPs that doesn't depend on the kubelet
	// setting them.
	if cfg.DisableCCM {
		dualStack, err := utilsnet.IsDualStackIPs(cfg.NodeIPs)
		if err == nil && !dualStack {
			argsMap["node-ip"] = cfg.NodeIP
		}
	} else {
		argsMap["cloud-provider"] = "external"
		if nodeIPs := util.JoinIPs(cfg.NodeIPs); nodeIPs != "" {
			argsMap["node-ip"] = util.JoinIPs(cfg.NodeIPs)
		}
	}

	kubeletRoot, runtimeRoot, controllers := cgroups.CheckCgroups()
	if !controllers["pids"] {
		return nil, nil, errors.New("pids cgroup controller not found")
	}
	if !controllers["cpu"] {
		logrus.Warn("Disabling CPU quotas due to missing cpu controller or cpu.cfs_period_us")
		defaultConfig.CPUCFSQuota = utilsptr.To(false)
	}
	if kubeletRoot != "" {
		defaultConfig.KubeletCgroups = kubeletRoot
	}
	if runtimeRoot != "" {
		argsMap["runtime-cgroups"] = runtimeRoot
	}

	argsMap["node-labels"] = strings.Join(cfg.NodeLabels, ",")

	if ImageCredProvAvailable(cfg) {
		logrus.Infof("Kubelet image credential provider bin dir and configuration file found.")
		argsMap["image-credential-provider-bin-dir"] = cfg.ImageCredProvBinDir
		argsMap["image-credential-provider-config"] = cfg.ImageCredProvConfig

View on GitHub (pinned to 6ba341e396)

Solutions

  1. On the host verify: grep pids /sys/fs/cgroup/cgroup.controllers (v2) or existence of /sys/fs/cgroup/pids (v1).
  2. Run k3s directly on the host or in a properly privileged container (host cgroup namespace: --cgroupns=host, full /sys mount).
  3. Enable the pids controller in systemd slices (ensure no cgroup hierarchy excludes pids) and prefer cgroup v2 unified mode.
  4. For WSL2/old kernels, upgrade to a kernel with pids controller support.

Example fix

# before: pids hidden inside container
docker run ... k3s server
# after: expose host cgroups
docker run --privileged --cgroupns=host -v /sys/fs/cgroup:/sys/fs/cgroup ... k3s server
Defensive patterns

Strategy: validation

Validate before calling

// Same probe CheckCgroups performs, as a pre-flight:
data, _ := os.ReadFile("/sys/fs/cgroup/cgroup.controllers") // cgroup v2
if !strings.Contains(string(data), "- pids") && !strings.Contains(string(data), "pids") {
	if _, err := os.Stat("/sys/fs/cgroup/pids"); err != nil { // cgroup v1 fallback
		return errors.New("pids controller unavailable: kubelet cannot run; fix host cgroups")
	}
}

Try / catch

if _, _, err := agent.KubeletArgs(cfg); err != nil {
	if strings.Contains(err.Error(), "pids cgroup controller not found") {
		// host-level fix: privileged container / cgroupns=host / enable pids; not retryable in-place
	}
}

Prevention

When it happens

Trigger: Host has cgroup v1 without the pids controller; k3s runs inside a container whose /sys/fs/cgroup view hides pids; kernel booted with cgroup_no_pids or a minimal controller set; some WSL2/older-LTS kernels.

Common situations: Running k3s in Docker without --cgroupns=host / privileged; minimal VMs or NAS kernels lacking pids; cgroup v1 hybrid systems predating pids support.

Related errors


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