Control Groups (cgroups) is a Linux kernel mechanism, introduced in 2.6.24 (2008), that organises processes into a hierarchy of named groups and uses controllers to account for and limit each group’s consumption of CPU time, memory, I/O bandwidth, and process count. Every container runtime in existence — Docker, containerd, CRI-O, Podman — uses cgroups to enforce the resource limits declared in a container spec (--memory, --cpus, requests.memory, limits.cpu). Every systemd service on a modern Linux system runs in its own cgroup slice. Without cgroups, a container or service could consume all available memory, CPU, or file descriptors, starving other workloads on the same host. The current production version is cgroups v2 (also written cgroupv2, unified hierarchy), stable since kernel 4.5 and the default on all major distributions since RHEL 9, Ubuntu 21.10, and Fedora 31.
The structural improvement of cgroups v2 over v1 is its unified hierarchy: in v1, each controller (cpu, memory, blkio, pids) mounted as a separate hierarchy at its own path, making cross-controller coordination difficult and policy inconsistent. In v2, all controllers share a single tree rooted at /sys/fs/cgroup, mounted as cgroup2. A cgroup in v2 is a directory; creating a subdirectory creates a child cgroup. Processes are assigned to a cgroup by writing their PID to cgroup.procs. Controllers are enabled per-level via cgroup.subtree_control: writing +memory +cpu to a parent’s cgroup.subtree_control enables those controllers for its immediate children. The no-internal-process rule requires that a cgroup with enabled controllers for its children cannot itself contain processes — it must delegate to leaf cgroups — enforcing a clean separation between structural nodes and leaf workload nodes. The key controllers in v2 are: memory (limits total memory use via memory.max, swap via memory.swap.max, and provides real memory pressure detection via memory.pressure — a major improvement over v1’s approximate OOM killer); cpu (weight-based CPU time sharing via cpu.weight and hard limits via cpu.max in the format quota period); io (per-device weight, BPS limits, and IOPS limits via io.weight, io.max); pids (maximum process/thread count via pids.max, preventing fork bombs); and cpuset (CPU and NUMA node pinning). The freezer (freeze/thaw all processes in a group atomically, used for checkpoint-restore and live migration) and hugetlb controllers are also available.
Kubernetes interacts with cgroups v2 through the container runtime (containerd, CRI-O), which creates a cgroup hierarchy per pod and per container under the kubelet’s own cgroup. The pod’s requests and limits fields map to cgroup settings: resources.requests.cpu sets cpu.weight for the scheduler’s soft guarantee, resources.limits.cpu sets cpu.max for the hard ceiling, and resources.limits.memory sets memory.max — breaching this limit triggers the Linux OOM killer against the container’s processes. Kubernetes 1.25 reached stable support for cgroup v2 (cgroup v2 feature gate graduated), enabling the QoS-based eviction and improved memory pressure handling that v2’s memory.pressure interface makes possible. systemd is the primary userspace manager of the cgroup tree on modern systems: it creates slices, scopes, and service units as cgroup subtrees, and Kubernetes node components run within the system.slice or a dedicated kubepods.slice. In the confidential computing stack, cgroups constrain the resource usage of QEMU and virt-launcher processes for KubeVirt and Kata Containers workloads on the host, and the guest kernel maintains its own independent cgroup hierarchy for workloads running inside the VM.
