Mandatory Kubernetes Migration to cgroup v2: Beyond a Kernel Parameter
A production runbook for migrating Kubernetes clusters to cgroup v2; troubleshooting failCgroupV1, Pressure Stall Information (PSI), resolving Java OOMKilled regressions, and zero-downtime node upgrades.

The strict enforcement of cgroup v2 in recent Kubernetes releases, with tools like kubeadm defaulting failCgroupV1=true, leaves no more room to postpone this transition. However, a common misconception among platform engineering teams is treating this migration as a mere one-line GRUB flag change. In high-density production environments, cgroup v2 directly impacts memory reclamation, OOMKilled behaviors, and container runtime orchestration.
Why cgroup v1 Architecture Was Deprecated
In the legacy v1 architecture, each resource controller (CPU, Memory, blkio) operated within a completely isolated, independent hierarchy. This caused severe operational blind spots: when a container accumulated heavy page cache from disk I/O, the memory controller could not coordinate with the blkio controller to throttle asynchronous writeback. The resulting friction frequently caused unexpected I/O freezes and erratic OOM killer invocations.
Under cgroup v2, a Single Unified Hierarchy is enforced, ensuring each process belongs to exactly one cgroup across all resource controllers.
Observability Breakthrough: Pressure Stall Information (PSI)
The premier technical benefit of cgroup v2 is access to Pressure Stall Information (PSI). Traditional metrics only reported resource utilization percentages, failing to distinguish between benign high memory usage and destructive memory stalls where threads wait on page reclamation. PSI quantifies the exact percentage of wall-clock time lost due to CPU, memory, or I/O starvation.
| Metric | cgroup v1 Architecture | cgroup v2 Architecture |
|---|---|---|
| Hierarchy | Independent trees per subsystem | Single unified resource tree |
| I/O Throttling | Desynchronized from memory page cache | Precise writeback throttling |
| Resource Stalls | Unavailable (utilization percentage only) | Complete PSI stall metrics |
| JVM Memory Accounting | Prone to container limit misdetection | Native container memory alignment |
Pre-Migration Validation Checklist
-
Verify Active Hierarchy on Nodes: Check the filesystem mount of the cgroup root:
stat -fc %T /sys/fs/cgroup/A return value of
cgroup2fsconfirms v2;tmpfsdenotes legacy v1. -
Container Runtime Configuration (containerd / CRI-O): Ensure
SystemdCgroup = trueis explicitly enabled in/etc/containerd/config.toml. Delegating cgroup slicing directly to runc rather than systemd under v2 causes runtime state desynchronization. -
Legacy Runtime and JDK Audit: Audit older container images. Java runtimes prior to OpenJDK 8u372 and 11.0.16 fail to parse cgroup v2 memory max limits, causing JVM to size its heap relative to total host physical RAM and triggering immediate OOM kills.
Recommended Zero-Downtime Rollout (SOP)
- Cordon and drain the target node using
kubectl drain. - Append
systemd.unified_cgroup_hierarchy=1to kernel boot options in/etc/default/gruband update GRUB. - Reboot node, verify Kubelet status, and inspect containerd integration.
- Deploy a test pod with strict memory constraints and verify
/sys/fs/cgroup/memory.current. - Uncordon node and return to active scheduling.
Operational Takeaway
Migrating to cgroup v2 is a foundational infrastructure improvement that stabilizes multi-tenant node performance. Validate workload runtimes in staging, monitor PSI stalls, and execute rollouts incrementally across node pools.



