Skip to main content
RunBook Academy

LinuxXXXVII · Resource Managementsystemd resource controls

systemd resource controls - applying cgroups via units

Intermediate⏱ ~12 minsystemdsystemd-cgtop

What you'll learn

  • Apply resource controls via systemd
  • Use CPU, Memory, IO directives
  • Configure slices for hierarchical limits
  • Inspect resource usage per service

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

systemd uses cgroups to manage resources for every service. Directives in the [Service] and [Slice] sections set limits. This lesson covers the production patterns.

Service-level directives

[Service]
# 2 CPUs
CPUQuota=200%
# hard limit
MemoryMax=4G
# soft limit; reclaim above
MemoryHigh=3G
# I/O weight (1-10000)
IOWeight=100
# max PIDs/threads
TasksMax=1024

Note where the comments sit. Each one is on a line of its own, above the directive it explains. That is not a style preference. It is the only form systemd accepts.

CPUQuota always takes a percentage

CPUQuota= takes a percentage value with a mandatory % suffix, where 100% is one full core. There is no bare-integer form:

# WRONG - "Invalid CPU quota '4', ignoring." The unit gets no quota.
CPUQuota=4

# RIGHT - 4 CPUs
CPUQuota=400%

A bare 4 is not read as “4 CPUs” and is not read as “4%”. It is rejected outright, so the service runs unconstrained on every core. Confirm the quota landed:

systemctl show my-service -p CPUQuotaPerSecUSec
# CPUQuota=400% -> CPUQuotaPerSecUSec=4s   (4 CPU-seconds per second)
# no quota       -> CPUQuotaPerSecUSec=infinity

Slice-level directives

Slices group services for hierarchical limits:

# /etc/systemd/system/production.slice
[Slice]
# 4 CPUs for all services in this slice
CPUQuota=400%
# 16 GB total for the slice
MemoryMax=16G

Place services in a slice:

# /etc/systemd/system/my-service.service
[Service]
Slice=production.slice
ExecStart=/usr/local/bin/my-service

Inspect

# Show resource usage per service
systemd-cgtop

# Detailed view of a service
systemctl status my-service

# cgroup filesystem view
ls /sys/fs/cgroup/system.slice/my-service.service/
cat /sys/fs/cgroup/system.slice/my-service.service/memory.current
cat /sys/fs/cgroup/system.slice/my-service.service/memory.events

Common production patterns

Web server with a budget

[Service]
CPUQuota=400%
MemoryMax=8G
MemoryHigh=6G
IOWeight=200
TasksMax=2048

Database with persistent memory

[Service]
# 8 CPUs
CPUQuota=800%
# hard limit; OOMs above this
MemoryMax=32G
# higher I/O priority
IOWeight=500

Background job with low priority

[Service]
# half a CPU
CPUQuota=50%
MemoryMax=2G
# lowest I/O priority
IOWeight=10
# lowest CPU scheduling priority
Nice=19

Hierarchical limits

Slices can be nested:

production.slice (4 CPUs, 16 GB)
├── web.slice (3 CPUs, 8 GB)
│   ├── nginx.service
│   └── app.service
└── db.slice (3 CPUs, 8 GB)
    └── postgresql.service

Inner slices cannot exceed their parent’s limits. This gives hierarchical resource control.

Memory tuning

To reduce the chance of an OOM kill:

[Service]
# soft limit; reclaim and throttling start here
MemoryHigh=3G
# hard limit; crossing it means a cgroup OOM kill
MemoryMax=4G
# MemorySwapMax is deliberately NOT set: swap is the escape valve

MemoryHigh triggers reclaim rather than an OOM kill, so the gap between the two values is your warning band. It is not a guarantee of survival: a service whose working set genuinely exceeds MemoryHigh is throttled hard enough to fail health checks, and it is still killed if it reaches MemoryMax.

CPU tuning

For CPU-bound services:

[Service]
# 4 CPUs; the % suffix is mandatory
CPUQuota=400%
# relative weight if CPU is contended
CPUWeight=200

CPUQuota is a hard limit. CPUWeight is relative.

Knowledge check

Knowledge check · 5 questions

  1. Q1. What is the difference between MemoryMax and MemoryHigh?

  2. Q2. Inner slices can exceed parent slice limits.

  3. Q3. Which of the following are valid systemd resource directives? Select all that apply.

  4. Q4. A colleague writes `MemoryMax=4G # hard limit` in a unit file, reloads, and starts the service. The service later consumes 30 GB and the host OOMs. What happened?

  5. Q5. You want to cap a batch service at four cores. Which directive does that?

Passing score: 75%. Answers are checked in this browser.