cgroups v2 Basics for Sysadmins
Introduction¶
cgroups (control groups) are how Linux accounts for and limits CPU, memory, and I/O per group of processes. Containers use them; systemd uses them for every service. Knowing the basics lets you cap a memory-hungry service or find which unit is eating the box.
Concepts¶
cgroups v2 replaced v1's separate per-controller hierarchies with one
unified hierarchy mounted at /sys/fs/cgroup. Every process is in exactly
one cgroup. Controllers (cpu, memory, io, pids) are enabled per
subtree.
systemd is the cgroup manager on modern distros. It creates:
system.slice/— system services, eachfoo.servicea cgroupuser.slice/user-1000.slice/— logged-in users and their sessionsmachine.slice/— VMs and containers
So nginx.service is already system.slice/nginx.service with its own
accounting. You just set limits on it.
Hands-on¶
See usage per unit¶
systemd-cgtop # like top, but by cgroup (CPU, memory, IO)
systemctl status nginx.service # shows the cgroup + current Memory/CPU/Tasks
systemd-cgls # the tree of cgroups and their processes
cat /sys/fs/cgroup/system.slice/nginx.service/memory.current
Cap a service¶
sudo systemctl edit myapp.service
[Service]
# Memory: throttle then OOM-kill the cgroup (not the whole box)
MemoryHigh=1500M # start reclaiming aggressively above this
MemoryMax=2G # hard limit — OOM-kills within the cgroup if exceeded
# CPU: 150% = 1.5 cores worth
CPUQuota=150%
CPUWeight=50 # relative share under contention (default 100)
# IO
IOWeight=50
IOReadBandwidthMax=/dev/sda 50M
# Process count (fork-bomb guard)
TasksMax=512
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
systemctl show myapp.service -p MemoryMax -p CPUQuota
MemoryMax is the safety valve: a leaking service gets OOM-killed inside its
own cgroup instead of the kernel killing something random on the host.
Limit a whole slice¶
Group related services and cap them together:
# /etc/systemd/system/batch.slice
[Slice]
CPUQuota=200%
MemoryMax=4G
Then Slice=batch.slice in each service. Everything in the slice shares the
4G / 2-core budget.
Transient limits (no unit file)¶
sudo systemd-run --scope -p MemoryMax=500M -p CPUQuota=50% ./heavy-import.sh
Verification and troubleshooting¶
# Was a service OOM-killed within its cgroup?
journalctl -u myapp.service | grep -i "oom\|killed"
cat /sys/fs/cgroup/system.slice/myapp.service/memory.events # oom, oom_kill counts
# Is it being CPU-throttled?
cat /sys/fs/cgroup/system.slice/myapp.service/cpu.stat # nr_throttled, throttled_usec
MemoryMaxset but service still takes down the box — you setMemoryHighonly (that just throttles), or the process is using shared memory / page cache attributed elsewhere, or an old kernel on cgroups v1. Checkcat /sys/fs/cgroup/system.slice/*/memory.current.CPUQuotaignored — thecpucontroller isn't enabled for that subtree. systemd enables it when any unit in the slice requests a CPU property;cat /sys/fs/cgroup/system.slice/cgroup.controllersshould listcpu.- Service restarts in a loop after hitting
MemoryMax— it's being OOM-killed andRestart=brings it back. Fix the leak or raise the limit; addStartLimitIntervalSec/StartLimitBurstto stop the loop. systemd-cgtopshows a slice eating CPU but no obvious service —systemd-cglsto expand it; oftenuser.slice(a runaway user process, a broken cron) ormachine.slice(a container).- Still on cgroups v1 —
stat -fc %T /sys/fs/cgroupsaystmpfs(v1) vscgroup2fs(v2). Addsystemd.unified_cgroup_hierarchy=1to the kernel cmdline; most distros since ~2021 are v2 by default.
Related tools and reading¶
- On-site: SAR Report Analyzer, Linux commands reference.
- Related posts: How systemd unit files work, Understanding Linux load average.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Linux & Sysadmin
resolv.conf and systemd-resolved
On modern distros /etc/resolv.conf points at a stub resolver managed by systemd-resolved. Editing it directly usually does nothing — here's the real model.
October 24, 2026Tuning Open File Limits on Linux
Four different limits control how many files a process can open. 'Too many open files' means you're hitting one specific one — here's how to find and raise it.
October 24, 2026ss and conntrack for Connection Debugging
ss shows you every socket and its send/receive queues; conntrack shows what the firewall is tracking. Together they explain most 'connection refused' and 'no buffer space' problems.
October 24, 2026