Stage 3 · Build
systemd Service Management & Resource Control
Resource Limits & cgroups
CPUQuota, MemoryMax, IOWeight, TasksMax, slices, and inspecting placement with systemd-cgls.
Resource Control Directives
systemd provides directives to control CPU, memory, and I/O resources for services. These directives map directly to cgroups v2 controllers.
| Directive | Controller | Purpose |
|---|---|---|
| CPUQuota | cpu | Limit CPU usage |
| CPUWeight | cpu | Proportional CPU sharing |
| MemoryMax | memory | Hard memory limit |
| MemoryHigh | memory | Soft memory limit (throttle) |
| MemorySwapMax | memory | Swap limit |
| IOWeight | io | Proportional I/O sharing |
| IOReadBandwidthMax | io | Read bandwidth limit |
| IOWriteBandwidthMax | io | Write bandwidth limit |
| TasksMax | pids | Maximum number of processes/threads |
CPU Limits
# CPUQuota: hard limit (percentage of one CPU)
[Service]
CPUQuota=50%
# Use at most 50% of one CPU
CPUQuota=200%
# Use at most 2 full CPUs
# CPUWeight: proportional sharing (1-10000, default 100)
[Service]
CPUWeight=50
# Half the CPU share of a default service
CPUWeight=200
# Double the CPU share of a default service
# Combine quota and weight
[Service]
CPUQuota=200% # Hard limit: 2 CPUs
CPUWeight=200 # Relative weight: double
# Check CPU limits
systemctl show myapp -p CPUQuota
# CPUQuota=200%
systemctl show myapp -p CPUWeight
# CPUWeight=200CPUQuota is a hard cap. CPUWeight determines proportional share when there is contention. Use CPUQuota for strict limits, CPUWeight for relative priorities.
Memory Limits
# MemoryMax: hard limit (OOM kill when exceeded)
[Service]
MemoryMax=1G
# MemoryHigh: soft limit (throttle when exceeded)
[Service]
MemoryHigh=512M
# When memory exceeds 512MB, the service is throttled
# When memory exceeds MemoryMax, the service is killed
# MemorySwapMax: limit swap usage
[Service]
MemorySwapMax=256M
# MemoryMin: guaranteed minimum (not reclaimable)
[Service]
MemoryMin=256M
# OOMPolicy
[Service]
OOMPolicy=kill
# kill (default), continue, or stop
# OOMScoreAdjust: OOM killer priority
[Service]
OOMScoreAdjust=-500
# -1000 to 1000 (higher = more likely to be killed)
# Check memory limits
systemctl show myapp -p MemoryMax
# MemoryMax=1073741824
systemctl show myapp -p MemoryCurrent
# MemoryCurrent=524288000MemoryHigh triggers throttling (graceful). MemoryMax triggers OOM kill (immediate). Use both: MemoryHigh for warning, MemoryMax for hard limit.
I/O Limits
# IOWeight: proportional I/O sharing (1-10000, default 100)
[Service]
IOWeight=50
# Half the I/O share of a default service
# IOReadBandwidthMax: read bandwidth limit
[Service]
IOReadBandwidthMax=/dev/sda 100M
# 100 MB/s read limit on /dev/sda
# IOWriteBandwidthMax: write bandwidth limit
[Service]
IOWriteBandwidthMax=/dev/sda 50M
# 50 MB/s write limit on /dev/sda
# IOReadLatencyMax: read latency target
[Service]
IOReadLatencyMax=/dev/sda 50000
# 50ms read latency target
# IODeviceWeight: device-specific weight
[Service]
IODeviceWeight=/dev/sda 200
# Check I/O limits
systemctl show myapp -p IOWeight
systemctl show myapp -p IOReadBandwidthMaxI/O limits are per-device. Use block device paths (8:0 for /dev/sda). IOWeight provides proportional sharing; bandwidth limits are hard caps.
Slices and Hierarchy
# Slices group services for resource control
# Hierarchy:
# -.slice (root)
# ├─system.slice
# │ ├─nginx.service
# │ └─postgresql.service
# └─user.slice
# └─user-1000.slice
# └─session-1.scope
# Create a custom slice
# /etc/systemd/system/myapp.slice
[Unit]
Description=MyApp Application Slice
Before=slices.target
[Slice]
CPUQuota=400%
MemoryMax=4G
IOWeight=200
TasksMax=4096
[Install]
WantedBy=multi-user.target
# Assign services to slice
# In service unit:
[Service]
Slice=myapp.slice
# Or at runtime
systemctl set-property myapp.service Slice=myapp.slice
# View slice resource usage
systemd-cgls
systemd-cgtopSlices provide hierarchical resource control. All services in a slice share the slice's resource limits. Use slices to group related services.
Inspecting cgroup Placement
# View cgroup hierarchy
systemd-cgls
# Control group /:
# -.slice
# ├─system.slice
# │ ├─nginx.service
# │ │ └─1234 nginx: worker process
# │ └─postgresql.service
# │ └─5678 postgres: writer process
# └─user.slice
# └─user-1000.slice
# └─session-1.scope
# └─12345 bash
# View resource usage
systemd-cgtop
# Path Tasks %CPU Memory Input/s Output/s
# /system.slice/nginx.service 5 0.5 12.5M 0 1.2M
# /system.slice/postgresql.service 8 2.3 256.0M 45.2K 12.8M
# Check which cgroup a process is in
cat /proc/12345/cgroup
# 0::/system.slice/nginx.service
# Inspect cgroup directly
ls /sys/fs/cgroup/system.slice/nginx.service/
# cpu.max memory.max io.weight pids.max ...
cat /sys/fs/cgroup/system.slice/nginx.service/memory.current
# 13107200systemd-cgls shows the cgroup tree. systemd-cgtop shows resource usage. Use these to verify service resource limits are applied.
Create a slice for each application stack (database, web server, cache). This groups related services and allows collective resource limits.
When a service exceeds MemoryMax, it is killed by the OOM killer. Always set MemoryMax higher than the service's typical memory usage with headroom for spikes.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.