Use Linux control groups (cgroups) to place a process or service in a hierarchy with explicit CPU, memory, I/O, and process-count policies. On a systemd host, the safest operational route is normally a systemd scope for a one-off command or a service unit for a daemon: CPUQuota= caps CPU time, MemoryMax= sets a hard memory boundary, and TasksMax= limits the number of processes and threads. Use the cgroup v2 files directly only when another supervisor owns the workload or you deliberately need kernel-level control.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
This guide shows how to identify cgroup v2, apply limits, verify that they are active, interpret throttling and out-of-memory events, and diagnose the hierarchy and delegation problems that make limits appear ineffective.
As an Amazon Associate I earn from qualifying purchases.
What a cgroup controls
A cgroup is a hierarchical group of processes to which the kernel applies resource policies. Every process belongs to a cgroup hierarchy, and a child group inherits effective constraints from its ancestors. The Linux kernel documentation describes a cgroup as a mechanism to organize processes hierarchically and distribute system resources along that hierarchy in a controlled, configurable manner.
In cgroup v2, one unified hierarchy exposes controller files such as cpu.max, memory.max, memory.high, io.max, and pids.max. Controllers must be enabled for a subtree before those files become usable. cgroup v2 became an official Linux interface with Linux 4.5, according to the 2025 edition of the Linux kernel man-pages project.
#1 Best Overall
A cgroup does not change an application’s code. It changes the scheduler, memory reclaim, block-I/O, and task-creation decisions made around that application. A limit set on a parent also constrains every descendant, so a child cannot consume resources that its ancestors have already withheld.
Choose the management layer first
| Method | Best for | Typical controls | What owns the cgroup |
|---|---|---|---|
| systemd transient scope | One command, test, build, or game server session | CPUQuota=, MemoryMax=, TasksMax= |
systemd until the scope exits |
| systemd service unit | Long-running daemons and boot services | CPU cap or weight, memory limit, I/O bandwidth, task limit | systemd and the unit hierarchy |
| Direct cgroup v2 files | Minimal systems, custom supervisors, or delegated subtrees | cpu.max, memory.max, memory.high, io.max, pids.max |
Your supervisor or script |
| Container runtime or Kubernetes | Containers and pods | Pod/container CPU and memory requests and limits | The runtime and kubelet, backed by host cgroups |
Do not have two managers write the same cgroup. The systemd cgroup v2 interface requires each individual cgroup to have a single writer. If systemd owns a service subtree, configure the unit rather than editing its files behind systemd’s back.
Check that the host uses cgroup v2
Run these checks before changing anything:
stat -fc %T /sys/fs/cgroup
cat /sys/fs/cgroup/cgroup.controllers
ps -e -o pid,comm,cgroup | head
A v2 host reports cgroup2fs from the stat command. The controller list shows which facilities the kernel exposes at the root. A controller can still be unavailable in your intended child if it has not been enabled in that child’s parent.
On a systemd machine, inspect the manager and a unit:
systemctl is-system-running
systemctl status example.service
systemctl show example.service -p ControlGroup -p CPUQuotaPerSecUSec -p MemoryMax -p TasksMax
The ControlGroup property tells you the unit’s path under /sys/fs/cgroup. Treat that path as systemd-owned. A rootless shell may see a delegated subtree rather than the host root; apply direct-file commands only where your user or supervisor has write permission.
Limit a one-off command with a transient systemd scope
A scope keeps the command in the caller’s session while systemd creates and manages its cgroup. This is usually the least error-prone way to test a policy:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →systemd-run --scope -p CPUQuota=50% -p MemoryMax=1G -- ./workload
- Start the scope. The command runs immediately and systemd prints a scope name.
CPUQuota=50%allows an aggregate maximum of half of one CPU over scheduling periods. A value of200%permits up to two CPUs’ worth of time, subject to ancestor limits and available capacity. - Observe the scope from another terminal. Replace the generated name in
systemctl status run-*.scope, or list active scopes withsystemctl list-units --type=scope. - Stop it cleanly when finished. Use
systemctl stop run-...scopeif the workload does not exit on its own. Stopping the scope terminates the processes systemd placed in it.
MemoryMax=1G maps to the cgroup v2 hard memory boundary. If reclaim cannot reduce usage below that boundary, the cgroup’s out-of-memory path can kill a process inside the group. It is a failure boundary, not a promise that every allocation is rejected at the exact byte shown by the setting.
Apply persistent limits to a service
Create or edit a unit drop-in rather than modifying a package-owned unit directly:
sudo systemctl edit example.service
Add a [Service] section such as:
[Service]
CPUQuota=50%
CPUWeight=200
MemoryMax=1G
IOReadBandwidthMax=/dev/nvme0n1 20M
TasksMax=256
Then load and restart the unit:
sudo systemctl daemon-reload
sudo systemctl restart example.service
systemctl show example.service -p CPUQuotaPerSecUSec -p CPUWeight -p MemoryMax -p IOReadBandwidthMax -p TasksMax
Pick a hard CPU cap or a proportional priority
CPUQuota= is a time-bandwidth ceiling. It limits aggregate CPU time over scheduler periods; it does not pin a process to a particular core or frequency and does not guarantee latency. A quota of 50% can still run on different cores at different times.
CPUWeight= is different: it expresses proportional scheduling priority when competing work is runnable. A service with a higher weight receives a larger share of available CPU than a sibling with a lower weight, but it can use more than its nominal share when the machine is otherwise idle. Use a quota when the workload must not exceed a ceiling; use a weight when you want fairness or priority without an absolute cap. You can set both when a service needs a maximum as well as a relative priority among work below that maximum.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Understand the other unit properties
MemoryMax=1Gsets the hard memory limit for the unit and its descendants.TasksMax=256limits the number of processes and threads that can exist in the unit’s subtree. It is useful against fork bombs and accidental thread explosions.IOReadBandwidthMax=/dev/nvme0n1 20Mlimits read bandwidth on the named block device. Use the device that actually carries the workload; a limit on another device has no effect.- All properties are hierarchical. A slice or parent unit with a smaller effective limit can constrain this service even when the service’s own value is larger.
To remove a drop-in and return to the vendor defaults, run sudo systemctl revert example.service, reload the manager, and restart the service. Confirm the resulting properties with systemctl show.
Use cgroup v2 files without systemd
Direct control is appropriate only for a cgroup that your supervisor owns or that has been explicitly delegated to you. The following example creates a child under the host root; run it as root on a test system and adapt the path for a delegated subtree.
- Create the group and enable controllers in its parent.
CG=/sys/fs/cgroup/workload sudo mkdir -p $CG echo '+cpu +memory +io +pids' | sudo tee /sys/fs/cgroup/cgroup.subtree_controlIf a controller is absent from
cgroup.controllers, the kernel configuration, boot mode, or delegation does not provide it. You cannot use an interface file for a controller that is not available and enabled. - Write the policy before moving the workload.
echo '50000 100000' | sudo tee $CG/cpu.max echo '1G' | sudo tee $CG/memory.max echo '768M' | sudo tee $CG/memory.high echo '256' | sudo tee $CG/pids.maxcpu.maxtakes a quota and period in microseconds. The pair above permits 50,000 microseconds of CPU time in each 100,000-microsecond period. Writingmax 100000removes the quota while retaining the period.memory.highis a reclaim and throttling boundary;memory.maxis the hard boundary.Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy. - Set an I/O rule for the correct device. Find the device’s major and minor numbers with
lsblk -o NAME,MAJ:MIN, then write a rule such as:
echo '259:0 rbps=20971520' | sudo tee $CG/io.max
The value is bytes per second, so 20 MiB/s is written as 20,971,520. Device names and major:minor numbers vary by machine; do not copy the example number blindly.
Rank #4
- Launch or move the process.
./workload & PID=$! echo $PID | sudo tee $CG/cgroup.procs wait $PIDWriting a PID to
cgroup.procsmoves that process; descendants created afterward remain under the cgroup. For a process tree already running, write each relevant PID or arrange for the parent to start inside the group. - Remove the group only after it is empty. Move remaining processes to the parent, then run
sudo rmdir $CG. A non-empty cgroup cannot be removed.
Verify enforcement and diagnose events
Configuration files show intent; event and usage files show what actually happened. From the group path, inspect:
sudo cat $CG/cpu.max
sudo cat $CG/cpu.stat
sudo cat $CG/memory.current
sudo cat $CG/memory.events
sudo cat $CG/io.stat
sudo cat $CG/pids.current
cpu.statincludes consumed CPU time and throttling counters such asnr_throttledandthrottled_usec. Rising throttling counters mean the quota is being reached, not that the process is bound to a core.memory.currentreports current accounted usage.memory.eventsrecords pressure and limit events, including high-boundary activity and maximum-limit or OOM-related events.io.statreports per-device I/O activity and lets you compare the device you limited with the device the workload actually uses.pids.currentshows the current process and thread count againstpids.max.
For systemd units, systemctl status, systemctl show, and systemd-cgtop provide unit-oriented views. Linux pressure-stall information in /proc/pressure/cpu, /proc/pressure/memory, and /proc/pressure/io helps distinguish a configured limit from broader host contention.
Recommended Free Tools
Memory limits: memory.high versus memory.max
| Setting | Behavior | When to use it | Failure signal |
|---|---|---|---|
memory.high |
Applies reclaim pressure and may throttle tasks crossing the boundary; it does not directly invoke the cgroup OOM killer. | Keep a service from growing unchecked while allowing it to recover under pressure. | High and pressure-related counters in memory.events, increased reclaim and latency. |
memory.max |
Hard ceiling. If usage cannot be reduced, the cgroup OOM path may kill a process inside the group. | Enforce a strict budget or isolate a workload from the rest of the host. | Maximum-limit and OOM-related events, followed by a killed process or failed allocation. |
Memory accounting includes major user-space allocations, page cache, and several kernel structures. The kernel documentation notes that the set of accounted objects can expand over time, so do not assume that only heap allocations count.
A maximum can be exceeded temporarily in some circumstances, and not every allocation path invokes the OOM killer. Monitor memory.current and memory.events instead of treating the configured number as an instantaneous byte-for-byte cutoff. Leave headroom for service overhead, shared libraries, and kernel-accounted memory when selecting a value.
Best Value
- New
- Mint Condition
- Dispatch same day for order received before 12 noon
- Guaranteed packaging
- No quibbles returns
Hierarchy, delegation, and common surprises
An ancestor is silently tighter
Effective limits are the intersection of every ancestor’s policy. A service set to MemoryMax=2G still cannot use 2G if its parent slice has a 1G maximum. Inspect the complete path from ControlGroup upward and read each ancestor’s controller files when the observed ceiling is lower than expected.
The controller file is missing
In v2, controllers are enabled top-down through cgroup.subtree_control. Check the parent’s cgroup.controllers, then enable the required controller in that parent. A file such as memory.max will not appear in a child until the memory controller is available and enabled there.
Free tools Windows power users keep installed
One-click scans. No signup required.
Another manager keeps undoing changes
Manual writes inside a systemd-owned unit can be overwritten during reloads, restarts, or service transitions. Configure the unit properties instead. Likewise, do not make a custom script and a container runtime manage the same delegated cgroup.
A limit appears ineffective
- Verify that the target PID is listed in the intended
cgroup.procs. - Check that the workload is using the device named in
io.max, not a different disk or a layered storage device. - Confirm that you are inspecting cgroup v2 rather than a legacy v1 mount.
- Read event counters while the workload is active; a static configuration value alone does not prove that the workload reached the limit.
- In rootless or containerized sessions, confirm that the runtime delegated the controller and that your process has write access to the subtree.
The workload is killed under memory pressure
Check memory.events and the service journal. If maximum-limit or OOM counters rise, raise memory.max, reduce concurrency, add a less aggressive memory.high boundary, or fix the workload’s allocation pattern. Do not respond by raising the limit blindly if the host or ancestor slice has a smaller effective budget.
How containers and Kubernetes use cgroups
Container runtimes and Kubernetes rely on cgroups to enforce pod and container CPU and memory settings. A pod limit is translated into cgroup configuration by the runtime and kubelet; the host’s cgroup mode, delegation, and runtime driver therefore affect what the pod experiences.
When a container behaves differently from its manifest, inspect the node rather than only the application:
- Check whether the node uses cgroup v2 with
stat -fc %T /sys/fs/cgroup. - Identify the runtime and kubelet cgroup driver and whether both use the same hierarchy.
- Inspect the container or pod cgroup’s
cpu.max,memory.max,memory.current, andmemory.eventswhere the runtime permits access. - Compare pod-level limits with ancestor limits imposed by the node, system slice, or container hosting the kubelet.
Do not edit a pod’s cgroup files behind the runtime. Change the pod specification or node configuration so the manager can preserve the policy across restarts and rescheduling.
A practical policy checklist
- Confirm cgroup v2 and identify the manager that owns the target hierarchy.
- Choose a scope for a one-off command, a service unit for a daemon, or a delegated subtree for direct control.
- Decide whether each resource needs a hard cap, throttling boundary, proportional weight, or protection from peers.
- Set CPU, memory, I/O, and task limits with explicit units and the correct device identifiers.
- Start the workload inside the cgroup and verify its PID membership.
- Watch usage, pressure, and event counters while the workload is busy.
- Adjust the policy based on observed throttling or reclaim behavior, preserving headroom for accounting and service overhead.
- Document the owner and rollback method so a later administrator does not introduce a second writer.
For most Linux services, systemd provides the clearest ownership and rollback path. Direct cgroup files are powerful when you control the hierarchy, but they require you to handle controller activation, process placement, delegation, and cleanup yourself.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




