Resource control with slices & cgroups

Cap CPU, memory, and IO per service.

Advanced12 min · lesson 7 of 17

systemd manages resources as well as lifecycle, because every service runs inside a cgroup (control group) it creates — the same kernel mechanism the container course uses. That means you can cap a service’s CPU, memory, and IO declaratively in its unit, and systemd enforces it via the cgroup. A runaway service can be confined so it cannot starve the rest of the host, without containers or external tools — just a few directives in the unit file.

THE CGROUP HIERARCHY systemd MANAGES
1root cgroup
the whole machine's resources
2slices
system.slice / user.slice / custom (shared budget)
3services
each in its own cgroup (CPUQuota, MemoryMax)
4processes
the tasks the kernel actually caps
Limits set at any level cap everything beneath it, so a whole slice can share one CPU/memory ceiling.
myapp.service (resource control)
[Service]
ExecStart=/usr/local/bin/myapp
CPUQuota=50% # at most half a core of CPU time
MemoryMax=512M # hard memory cap — OOM-killed if exceeded
MemoryHigh=400M # soft cap — throttled/reclaimed above this
TasksMax=100 # cap the number of processes/threads (anti fork-bomb)
IOWeight=50 # relative disk IO priority

Slices: grouping and hierarchy

cgroups are hierarchical, and systemd organizes services into slices — a tree you can apply limits to at any level. All user sessions live under user.slice, system services under system.slice, and you can create your own slice (say, batch.slice) and place several services in it with a shared budget, so a whole category of work is collectively capped. This is how you give, for example, all background jobs a combined CPU ceiling regardless of how many there are.

terminal
$ systemd-cgls | head # the cgroup tree: slices → services → processes
$ systemd-cgtop # live CPU/memory/IO per cgroup (like top, but by service)
Control Group Tasks %CPU Memory
system.slice 142 68.2 4.1G
system.slice/myapp 12 41.0 480M # this one service’s real usage

Inspect and adjust live

You do not have to reboot to see or change limits. systemctl show exposes the effective resource settings, systemd-cgtop is a top-like live view grouped by service (invaluable for "which service is eating the box?"), and systemctl set-property applies a limit to a running service immediately. Between the unit-file directives for permanent policy and set-property for live intervention, you can both design and firefight resource contention with systemd alone.

terminal
$ systemctl show myapp -p MemoryMax -p CPUQuotaPerSecUSec # effective limits
$ sudo systemctl set-property myapp.service MemoryMax=256M # apply NOW, live
$ systemd-cgtop # watch the effect
MemoryMax OOM-kills; MemoryHigh throttles
Know the difference before you set a memory limit in production. MemoryMax is a hard wall — exceed it and the service is OOM-killed, which for a stateful process can mean a crash and recovery. MemoryHigh is a soft limit that aggressively reclaims and throttles the service above it without killing it, giving back-pressure instead of death. Use MemoryHigh to keep a service in bounds gracefully and MemoryMax as the safety backstop above it — setting only a tight MemoryMax can turn a memory spike into repeated kills.