Enforcing CPU Hard Limits with Linux cgroup v2
Learn how to use cgroup v2 cpu.max to enforce hard CPU limits on Linux processes, preventing noisy neighbors from saturating system resources.
16 Sept 2025, 16:59 UTC

The Problem: CPU Starvation and Noisy Neighbors
In multi-tenant environments or when running background workers, a single runaway process can saturate all available CPU cycles. While nice values adjust priority, they do not prevent a process from consuming 100% of the CPU if no other higher-priority tasks are demanding it. To strictly cap a process's CPU consumption regardless of system load, you must use Control Groups v2 (cgroup v2).
The takeaway: Use the cpu.max controller in cgroup v2 to define a hard quota of CPU time per period, ensuring a process cannot exceed a specific percentage of a single core's capacity.
How cgroup v2 CPU Capping Works
Unlike cgroup v1, which had separate hierarchies for different resources, cgroup v2 uses a unified hierarchy. This means a process belongs to one group, and all resource controllers (CPU, Memory, I/O) are applied to that same group.
CPU limits are governed by two values in the cpu.max file: the quota and the period. The period is the window of time (usually 100ms), and the quota is the maximum amount of time within that window the group is allowed to run. For example, if the period is 100,000 microseconds (100ms) and the quota is 50,000 microseconds, the process is capped at 50% of one CPU core.
Worked Configuration Example
This example creates a restricted group for a background worker, capping it at 20% of a single CPU core. This requires root permissions as it modifies the /sys/fs/cgroup filesystem.
# 1. Create a new cgroup directory (this automatically initializes controllers)
sudo mkdir /sys/fs/cgroup/worker_limit
# 2. Set the CPU limit
# Format: "$MAX $PERIOD"
# 20,000us quota / 100,000us period = 20% of one core
sudo sh -c "echo '20000 100000' > /sys/fs/cgroup/worker_limit/cpu.max"
# 3. Move a running process into the group
# Replace [PID] with the actual process ID
sudo sh -c "echo [PID] > /sys/fs/cgroup/worker_limit/cgroup.procs"
Verification and Diagnostics
To verify the limit is active, run a CPU-intensive task (like stress or a while true loop) inside the group and monitor it from a separate terminal using top or htop. The process should plateau at exactly 20% CPU usage, even if the rest of the system is idle.
You can also check the cpu.stat file in the group directory to see how many microseconds the process was throttled:
cat /sys/fs/cgroup/worker_limit/cpu.stat
# Look for 'nr_throttled' and 'throttled_usec'
Critical Constraints and Common Mistakes
The "No Internal Process" Rule
One of the most common points of failure in cgroup v2 is the no internal process rule. In v2, a parent cgroup cannot contain both processes and child cgroups. If you attempt to write a PID to cgroup.procs in a directory that already has subdirectories (child groups), the kernel will return an error. Processes must reside in leaf nodes.
Multi-core Calculations
If you want to cap a process to 200% (the equivalent of 2 full cores), you must set the quota to twice the period:
- Target: 200% CPU
- Configuration:
echo '200000 100000' > cpu.max
Quota vs. Weight
Do not confuse cpu.max with cpu.weight.
cpu.maxis a hard limit. The process is throttled even if the CPU is 99% idle.cpu.weightis a relative share. It only restricts the process when other groups are competing for the same CPU cycles.
Rollback and Cleanup
To remove the limits and return the process to the root cgroup, move the PID back to the root cgroup.procs and delete the directory.
# Move process back to root
sudo sh -c "echo [PID] > /sys/fs/cgroup/cgroup.procs"
# Remove the group
sudo rmdir /sys/fs/cgroup/worker_limit
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.