Control resources with cgroup v2
Control groups (cgroups) let the Linux kernel limit, prioritise and account for
the resources a group of processes can use. In the version 2 unified hierarchy,
each resource type is handled by a controller — cpu, memory, io, pids,
cpuset, hugetlb, rdma and misc — exposed as plain files under
/sys/fs/cgroup. This tool lists every controller with its interface files
(knobs), showing which are read-only counters and which are writable limits.
How it works
A cgroup is a directory under /sys/fs/cgroup. Inside it you find the interface
files for the controllers its parent has delegated. Limits follow a consistent
naming scheme: <controller>.max is the hard ceiling, <controller>.current
reports live usage, and <controller>.stat gives a detailed breakdown. For a
controller to work in a child, the parent must list it in
cgroup.subtree_control (written with a + prefix). The cpu and io
controllers also support proportional sharing through a weight knob, while
cpuset pins tasks to specific CPUs and NUMA nodes. systemd creates and manages
most cgroups on a modern system, mapping unit options like MemoryMax= onto
memory.max.
Each controller in brief
cpu — Controls CPU time allocation. cpu.weight (1–10000, default 100) sets proportional sharing. cpu.max sets a hard quota: the two-number format $QUOTA $PERIOD means “up to $QUOTA microseconds of CPU per $PERIOD microseconds.” Writing 50000 100000 caps the group at exactly half a CPU. cpu.stat exposes usage, throttle time, and burst.
memory — The most commonly used controller. memory.max is the hard limit triggering OOM kills; memory.high is a soft ceiling that triggers reclaim and slows allocation before reaching max. memory.current gives live resident-set usage. memory.swap.max gates swap separately. memory.stat breaks down anonymous, file, and kernel memory.
io — Throttles block device throughput and IOPS per device. Device keys are MAJ:MIN pairs from lsblk. io.max sets hard limits (rbps=, wbps=, riops=, wiops=). io.weight enables proportional sharing when io.bfq.weight is supported. io.stat accumulates read/write bytes and operations per device.
pids — Caps the number of processes (tasks) the group can contain. pids.max is the limit; pids.current is the live count. This is the fastest protection against fork bombs and runaway job queues.
cpuset — Pins tasks to a specific set of CPUs (cpuset.cpus) and NUMA memory nodes (cpuset.mems). Essential in NUMA systems and for isolating latency-sensitive tasks from background workloads.
hugetlb — Limits consumption of huge-page memory for groups that use mmap with huge pages. Useful in database and HPC workloads.
rdma — Controls RDMA device resources (handles, memory regions) for InfiniBand and RoCE workloads.
misc — A generic bucket for other hardware resources the kernel chooses to expose, such as DMA buffers. Less commonly configured directly.
Worked example — memory cap with a soft throttle
# Create a cgroup for a batch job
mkdir /sys/fs/cgroup/batchjob
# Delegate memory controller from the parent
echo "+memory" > /sys/fs/cgroup/cgroup.subtree_control
# Set a soft cap at 512 MiB (triggers reclaim)
echo 536870912 > /sys/fs/cgroup/batchjob/memory.high
# Set a hard cap at 768 MiB (triggers OOM kill)
echo 805306368 > /sys/fs/cgroup/batchjob/memory.max
# Move the shell (and any children it spawns) into the cgroup
echo $$ > /sys/fs/cgroup/batchjob/cgroup.procs
With this setup the batch job is free to run up to 512 MiB without interruption. Beyond that, the kernel applies memory pressure via reclaim. If usage reaches 768 MiB, the OOM killer intervenes — protecting the rest of the system.
Tips and notes
Treat memory.high as your first line of defence — it throttles before the hard
memory.max triggers an OOM kill. For I/O throttling, io.max takes per-device
entries keyed by MAJ:MIN, so look the device numbers up with lsblk first.
Pressure files (*.pressure) expose PSI stall metrics and are invaluable for
spotting CPU, memory or I/O contention before it becomes a hang. Always check
<controller>.controllers and cgroup.subtree_control if a knob you expect is
missing — the controller may simply not be delegated to that cgroup.