The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Confirm cgroup v2stat -fc %T /sys/fs/cgroupView examples
Show current cgroupcat /proc/self/cgroupView examples
List available controllerscat /sys/fs/cgroup/cgroup.controllersView examples
Find unit cgroupsystemctl show api.service -p ControlGroupView examples
Run a limited scopesystemd-run --user --scope -p MemoryHigh=768M -p \ MemoryMax=1G -p CPUQuota=150% make -j4View examples
Apply memory throttleMemoryHigh=768MView examples
Apply hard memory limitMemoryMax=1GView examples
Read memory eventscat \ /sys/fs/cgroup/system.slice/api.service/memory.eventsView examples
Set relative CPU weightCPUWeight=200View examples
Cap CPU bandwidthCPUQuota=150%View examples
Set relative I/O weightIOWeight=200View examples
Limit tasksTasksMax=512View examples
List member processescat /sys/fs/cgroup/system.slice/api.service/cgroup.procsView examples
Enable child controllersprintf '%s\n' '+cpu +memory +pids' | sudo tee \ /sys/fs/cgroup/ops.slice/cgroup.subtree_controlView examples
Watch cgroup usagesystemd-cgtop --depth=3View examples
Read CPU pressurecat /sys/fs/cgroup/system.slice/api.service/cpu.pressureView examples

cgroup v2 organizes processes in one hierarchy and applies hierarchical resource policy through controllers. In production, let the system service manager own that hierarchy: express durable limits in units, use transient scopes or services for experiments, and reserve direct cgroupfs writes for delegated subtrees or controlled diagnostics. A limit without monitoring is incomplete—track throttling, pressure, out-of-memory events, and workload latency before and after every change.

Step by step

Detailed examples

01

Confirm the unified hierarchy and controller availability

A cgroup2fs mount indicates v2 at the conventional path, while /proc/self/cgroup shows the caller's path. cgroup.controllers lists controllers available for distribution at that node; a controller may be absent because of kernel configuration, a boot-time v1 assignment, or an ancestor's policy. Do not infer effective limits from a single file: restrictions compose top-down and a descendant cannot escape an ancestor's ceiling.

Inventory cgroup v2 without changing state
stat -fc 'filesystem: %T' /sys/fs/cgroup
cat /proc/self/cgroup
printf 'available: '; cat /sys/fs/cgroup/cgroup.controllers
printf 'enabled for children: '; cat /sys/fs/cgroup/cgroup.subtree_control
Back to quick reference ↑
02

Use systemd as the production cgroup owner

systemd creates and removes cgroups with unit lifecycle, translates resource properties to the kernel interface, and preserves hierarchy invariants. Put durable settings in a service or slice; use systemd-run for a transient experiment. A user manager can apply only controllers and limits delegated to it, and a transient scope inherits ceilings from its ancestors. Confirm the resulting ControlGroup and effective properties rather than assuming every requested controller was available.

Create and inspect a constrained user scope
systemd-run --user --scope --unit=build-limited.scope \
  -p MemoryHigh=768M \
  -p MemoryMax=1G \
  -p CPUQuota=150% \
  make -j4
systemctl --user show build-limited.scope -p ControlGroup -p MemoryHigh -p MemoryMax -p CPUQuotaPerSecUSec
Back to quick reference ↑
03

Throttle first, reserve the hard ceiling for containment

memory.high applies reclaim pressure and throttles allocations without invoking the cgroup OOM killer; it is the primary operational control when a monitor can respond. memory.max is a hard containment boundary and may trigger OOM killing when reclaim cannot satisfy it. Leave headroom, load-test realistic working sets, decide whether an entire unit should be treated as one OOM group, and alert on memory.events deltas rather than only current byte usage.

Configure a monitored service memory envelope
[Service]
MemoryAccounting=yes
MemoryHigh=768M
MemoryMax=1G
MemorySwapMax=256M
OOMPolicy=stop
Inspect usage and outcome counters
unit_path=/sys/fs/cgroup/system.slice/api.service
cat "$unit_path/memory.current"
cat "$unit_path/memory.peak"
cat "$unit_path/memory.events"
Back to quick reference ↑
04

Distinguish relative weights from absolute bandwidth ceilings

CPUWeight and IOWeight influence distribution only when sibling cgroups compete for the same resource; they do not reserve capacity. CPUQuota imposes a bandwidth ceiling, where 100% corresponds to one CPU of runtime and values above 100% permit multiple CPUs. I/O controls depend on controller availability, device topology, and scheduler support. Benchmark tail latency and throughput because aggressive CPU quotas or I/O caps can amplify queueing.

Express service-level contention policy
[Service]
CPUAccounting=yes
CPUWeight=200
CPUQuota=150%
IOAccounting=yes
IOWeight=200
Back to quick reference ↑
05

Bound process creation and monitor rejected forks

The pids controller counts tasks across a cgroup and its descendants. TasksMax maps systemd policy to pids.max and blocks fork or clone with EAGAIN when a new task would violate the limit. Existing membership can temporarily exceed a newly lowered maximum, and moving processes is not a way to test fork behavior. Size the limit for worker bursts, helper processes, and thread-heavy runtimes, then alert on pids.events max increments.

Set and observe a service task budget
[Service]
TasksAccounting=yes
TasksMax=512
Read task count and limit events
unit_path=/sys/fs/cgroup/system.slice/api.service
cat "$unit_path/pids.current"
cat "$unit_path/pids.max"
cat "$unit_path/pids.events"
Back to quick reference ↑
06

Respect top-down control and the no-internal-process rule

Controllers are enabled for children through cgroup.subtree_control. A non-root domain cgroup can distribute domain controllers only when it contains no processes itself, keeping workloads in leaves. Moving a PID through cgroup.procs moves all threads of that process, but already charged memory is not migrated with it. Direct writes under /sys/fs/cgroup can conflict with systemd and should occur only inside a formally delegated subtree with ownership, containment, cleanup, and controller boundaries designed in advance.

Review a low-level delegated-subtree sequence
# CAUTION: privileged cgroupfs writes alter live scheduling and memory policy.
# Use only for a documented subtree that the system manager delegated to ops.slice.
cat /sys/fs/cgroup/ops.slice/cgroup.controllers
printf '%s\n' '+cpu +memory +pids' | sudo tee /sys/fs/cgroup/ops.slice/cgroup.subtree_control
cat /sys/fs/cgroup/ops.slice/cgroup.subtree_control
Back to quick reference ↑
07

Correlate counters, pressure, throttling, and application latency

systemd-cgtop provides a live hierarchy view when accounting is available. cpu.stat exposes usage and throttling, memory.events reports pressure and OOM outcomes, and PSI files quantify time stalled on CPU, memory, or I/O. Counters are cumulative and some events are hierarchical, so monitoring should store rates, distinguish local from descendant events where local files exist, and correlate every policy adjustment with service-level indicators.

Capture a read-only resource-control snapshot
unit_path=/sys/fs/cgroup/system.slice/api.service
systemctl show api.service -p ControlGroup -p MemoryCurrent -p MemoryPeak -p TasksCurrent
cat "$unit_path/cpu.stat"
cat "$unit_path/cpu.pressure"
cat "$unit_path/memory.events"
cat "$unit_path/io.pressure"
Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. Linux kernel documentationControl Group v2kernel.org
  2. Linux man-pages projectcgroups(7) — Linux control groupsman7.org
  3. systemd projectsystemd.resource-controlfreedesktop.org
  4. systemd projectsystemd-runfreedesktop.org
  5. systemd projectsystemd-cgtopfreedesktop.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback