Using cgroups v2 to Limit CPU and Memory for Systemd Services
Configure CPU and memory limits for systemd services using Linux cgroups v2. Verify the unified hierarchy, create a slice, assign services, and monitor usage with systemd-cgtop.
30 Oct 2025, 00:18 UTC

Problem Statement
On modern Linux distributions, many services run under systemd and share the same cgroup hierarchy. Without explicit limits, a single misbehaving process can consume all CPU or memory, destabilising the host. cgroups v2 provides a unified hierarchy that simplifies resource isolation, but many administrators still use legacy cgroups v1 or are unsure how to configure limits via systemd.
Desired Outcome
Define CPU and memory limits for a service using cgroups v2, verify the limits are enforced, and monitor real‑time usage without altering the system’s global configuration.
Prerequisites
- Kernel 5.4 or newer (cgroups v2 support is complete).
- Root or sudo privileges to edit unit files and write to
/sys/fs/cgroup. - Service already installed (e.g.,
myapp.service). - Ensure the system is running in pure cgroups v2 mode:
/sys/fs/cgroup/cgroup.controllersmust exist.
Step 1: Verify cgroups v2 is Active
# ls /sys/fs/cgroup/cgroup.controllers
cpu memory
If the directory does not exist, the kernel is in cgroups v1 or hybrid mode. Switch to v2 by adding systemd.unified_cgroup_hierarchy=1 to /etc/default/grub (or the appropriate kernel command line) and reboot. Verify again after reboot.
Step 2: Create a Systemd Slice with Limits
Systemd slices are lightweight cgroup containers that can hold other units. Create myapp.slice in /etc/systemd/system/:
[Slice]
# Set a hard memory limit of 512MiB
MemoryMax=512M
# Cap CPU to 25% (quota: 25000µs, period: 100000µs)
CPUQuota=25%
Reload systemd to pick up the new slice:
# systemctl daemon-reload
Step 3: Assign Services to the Slice
Edit the service unit to place it inside the slice. Add or modify the Slice directive:
[Service]
Slice=myapp.slice
Reload the unit and restart the service:
# systemctl daemon-reload
# systemctl restart myapp.service
Verify the service is in the correct cgroup by inspecting /proc//cgroup:
# cat /proc/$(pgrep -f myapp)/cgroup
2:memory:/myapp.slice
2:cpu:/myapp.slice
Step 4: Monitor Resource Usage
Use systemd-cgtop to see real‑time consumption:
# systemd-cgtop -t 2 -p myapp.service
Alternatively, read the controller files directly:
# cat /sys/fs/cgroup/myapp.slice/memory.current
# cat /sys/fs/cgroup/myapp.slice/cpu.stat
Step 5: Adjust Limits if Needed
To increase memory to 1GiB, edit myapp.slice:
MemoryMax=1G
Reload and restart the slice:
# systemctl daemon-reload
# systemctl restart myapp.slice
For CPU, use CPUQuota=50% or the numeric form CPUQuota=50000 100000 (quota, period).
Rollback / Recovery
If a service becomes OOM‑killed due to an overly aggressive MemoryMax, set it to max to remove the limit temporarily, then revisit the value. For CPU, a sudden spike can be mitigated by increasing the period or reducing the quota.
Common Pitfalls and Mitigations
- Hybrid mode confusion: Mixing v1 and v2 can cause processes to be counted twice. Verify
/proc/self/cgroupshows only a single hierarchy. - OOM killer activation: A
MemoryMaxtoo low can kill legitimate processes. Monitor/sys/fs/cgroup/myapp.slice/memory.eventsforoom_killevents. - CPU latency spikes: Tight quotas can cause bursts of latency. Test under load using
stressbefore deploying to production. - Unit reload failure: Forgetting
systemctl daemon-reloadafter editing a slice will leave old limits in effect.
Example: Limiting a Web Server
Suppose nginx.service should not exceed 256MiB memory and 20% CPU. Create nginx.slice:
[Slice]
MemoryMax=256M
CPUQuota=20%
Assign the service:
[Service]
Slice=nginx.slice
Reload and restart:
# systemctl daemon-reload
# systemctl restart nginx.service
Verify limits:
# systemd-cgtop -t 2 -p nginx.service
# cat /sys/fs/cgroup/nginx.slice/memory.max
256000000
Now nginx will be automatically throttled when it approaches the defined limits, protecting the host from resource starvation.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.