
How to Migrate Kubernetes Nodes to cgroup v2 Before v1.38
Enterprise Developer PlatformsSummary
- Starting with Kubernetes v1.35, the kubelet setting
failCgroupV1defaults totrue, so the kubelet does not start on a cgroup v1 node unless you override it - In kubeadm clusters, the v1.35 preflight check now fails
kubeadm init,join, andupgradeon cgroup v1 nodes instead of warning - The cgroup v1 fallback still works in v1.36 but is scheduled for removal in v1.38 (KEP-5573)
- cgroup v2 support has been stable since v1.25 and unlocks Memory QoS, PSI metrics, container-aware OOM handling, and accurate Pod-level resource enforcement
- Most migration risk sits outside Kubernetes: kernel versions, container runtimes, and agents that read the cgroup filesystem directly
Purpose
To move every Linux node in a cluster to cgroup v2 before an upgrade turns a deprecation warning into kubelets that will not start. The Kubernetes project’s guidance is direct: clusters below v1.35 should migrate every node before upgrading, and clusters already on v1.35 or later should confirm every node runs cgroup v2 or that the override is deliberate.
Pre-requisites
- Root access to each Linux node, or a node image pipeline that can rebuild them
- Linux kernel 5.8 or later (5.9 or later if you plan to enable Memory QoS)
- A cgroup v2-capable container runtime: containerd v1.4+ (v2.0+ for automatic cgroup driver discovery) or CRI-O v1.20+
- An inventory of DaemonSets and agents on nodes: monitoring, security, and service mesh components
Steps
-
Audit what every node runs today. On each node, run
stat -fc %T /sys/fs/cgroup/. Output ofcgroup2fsmeans cgroup v2. Output oftmpfsmeans cgroup v1, per the Kubernetes cgroup documentation. Record node pools, OS images, and kernel versions alongside the result. In managed clusters, check provider defaults per node type, because they can differ within one cluster. -
Fix the kernel and OS image first. Kubernetes documents 5.8 as the minimum kernel for cgroup v2 and does not recommend anything older than 5.2. Memory QoS needs 5.9 or later to avoid a known
memory.highlivelock. Prefer rebuilding node images with cgroup v2 enabled over toggling hosts in place. -
Align the cgroup driver. The kubelet and container runtime must use the same cgroup driver. For kubeadm-managed clusters, Kubernetes recommends the
systemddriver. With containerd v2.0+ or CRI-O v1.28+, the kubelet detects the runtime’s driver automatically (stable since v1.34). On older runtimes, setcgroupDriver: systemdexplicitly in the KubeletConfiguration. -
Upgrade everything that reads cgroup files. This is where migrations break quietly. The guidance recommends cAdvisor v0.43.0 or later and lists minimum Java, Node.js, and automaxprocs versions. KEP-5573 cites OpenJDK jdk8u372, 11.0.16, or 15+, and Node.js 20.3.0+. Older runtimes can misread container memory and CPU limits under cgroup v2. Confirm third-party monitoring and security agents support v2 before rolling nodes.
-
Review the OOM behavior change. On cgroup v2, the kubelet defaults
singleProcessOOMKilltofalseand setsmemory.oom.group, so an OOM kills every process in the container together. For multi-process containers that relied on one worker dying while the rest kept serving, setsingleProcessOOMKill: trueonly where that v1-style behavior is genuinely required. -
Check CPU weight expectations. cgroup v1 uses
cpu.sharesand v2 usescpu.weight. Newer runtimes (crun v1.23, runc v1.3.2) use an improved non-linear conversion. Policy or monitoring tools that predict exactcpu.weightvalues may need updating after a runtime upgrade. -
Roll node pools and verify. Cordon, drain, and replace nodes pool by pool. On a migrated node, confirm limits landed by reading
cpu.maxandmemory.maxin the container’s cgroup directory, and usesystemd-cglsorsystemd-cgtopto inspect the hierarchy. -
Treat the override as a dated exception. If one pool cannot move before a v1.35+ upgrade, set
failCgroupV1: falsein that pool’s kubelet configuration, record an owner and a removal date, and plan to finish before v1.38 removes the fallback.
The Trap
Assuming the migration is a node OS setting. The kubelet and runtime switch cleanly. The failures come from the long tail: an old JVM reporting the host’s memory instead of the container limit, or a monitoring agent that silently stops reporting per-container metrics. Staging the change on one node pool that runs a representative mix of DaemonSets catches these before production does. For a related look at kubelet-level resource controls that depend on cgroup v2, see Stack Archive’s guide to Kubernetes v1.36 Pod-level resource managers.
Cited References
- Kubernetes Blog: The Shift to cgroup v2 in Kubernetes: What You Need to Know
- Kubernetes Documentation: About cgroup v2
- KEP-5573: Remove cgroup v1 support
Source: Kubernetes Blog
Content created with AI assistance and reviewed for accuracy.
Join the conversation
Stack Insiders is our free community for readers who want to go deeper — share resources, ask questions, and connect with others across every vertical we cover.
Join Stack Insiders →