Free tools Windows power users keep installed
One-click scans. No signup required.
Use systemd resource controls for services and one-off commands, or write to the cgroup v2 control files directly when systemd is not managing the workload. A CPU quota limits aggregate CPU time, MemoryMax= (or memory.max) sets a hard memory boundary that can lead to a cgroup OOM kill, and settings such as CPU weight, memory.high, I/O limits, and task limits provide softer forms of control. The procedure below shows how to identify the host’s cgroup hierarchy, apply limits safely, and verify what the kernel actually did.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
What a cgroup controls
A control group, or cgroup, is a hierarchical group of processes with policies for distributing system resources. Every process belongs to a cgroup hierarchy, and child groups inherit constraints from their ancestors. The Linux kernel documentation describes a cgroup as “a mechanism to organize processes hierarchically and distribute system resources along the hierarchy in a controlled and configurable manner.”
As an Amazon Associate I earn from qualifying purchases.
In cgroup v2, one unified hierarchy exposes controller files for CPU, memory, I/O, process counts, and other resources. Typical files include:
cpu.maxfor a hard CPU bandwidth quota.cpu.weightfor proportional CPU scheduling priority.memory.maxfor a hard memory ceiling.memory.highfor reclaim pressure and throttling before a hard limit.io.maxfor per-device I/O limits.pids.maxfor the maximum number of processes and threads.
These controls are hierarchical. If a parent allows 1 GB of memory, a child configured for 2 GB still cannot use more than the parent’s effective allowance. The same principle applies to CPU, I/O, and process-count constraints.
#1 Best Overall
Choose the management layer
Use one manager for each cgroup. On most modern Linux distributions, systemd owns the cgroup v2 hierarchy and creates a unit cgroup for each service, scope, slice, session, and user. Writing competing policies into the same group from a second manager can produce errors or settings that are overwritten. systemd’s cgroup interface requires each individual cgroup to have a single writer.
| Approach | Best for | Control style | Scope and observation |
|---|---|---|---|
| Transient systemd scope | A command that should be limited for one run | Properties such as CPUQuota= and MemoryMax= |
One command; inspect with systemctl status and cgroup files |
| Service unit properties | Long-running daemons | Hard caps, weights, protection, I/O, and task limits | A named service; managed through systemctl |
| Direct cgroup v2 files | Systems without systemd or specialized supervisors | Kernel controller files such as cpu.max and memory.max |
A manually created hierarchy; inspect files and event counters |
| Container runtime or Kubernetes | Pods and containers | Runtime settings translated into cgroup policies | Container or pod scope; verify runtime driver and host hierarchy |
For a systemd host, start with a transient scope or a unit drop-in. Direct file manipulation is appropriate only when you control the hierarchy and understand who owns it.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Check that the host uses cgroup v2
Before applying a policy, identify the hierarchy and the controllers available on the host.
- Check the filesystem type mounted at
/sys/fs/cgroup:stat -fc %T /sys/fs/cgroupA cgroup v2 host reports
cgroup2fs. A different result usually indicates a legacy or hybrid setup, where the v2-only file names in this guide may not exist. - List controllers available at the hierarchy root:
cat /sys/fs/cgroup/cgroup.controllersCommon entries include
cpu,memory,io, andpids. Availability depends on the kernel configuration and how the hierarchy was delegated. - Determine whether systemd owns the tree. On a systemd host, inspect the process and unit cgroup paths with:
systemctl status example.service systemd-cglssystemd-cglsdisplays the hierarchy;systemd-cgtopprovides a live resource view.
Do not enable controllers or move processes inside a systemd-owned group unless the operation is part of systemd’s delegation model. A policy applied in the wrong group may affect only a shell process, not the daemon you intended to constrain.
Limit a one-off command with systemd
A transient scope gives a command a cgroup without requiring a permanent unit file. The following command starts ./workload with a 50 percent CPU quota and a 1 GiB hard memory limit:
systemd-run --scope -p CPUQuota=50% -p MemoryMax=1G -- ./workload
CPUQuota=50% permits up to half of one CPU on average over the scheduler’s accounting periods. It does not pin the process to a core, select a CPU frequency, or guarantee response latency. A multithreaded workload can use more than half of one core at an instant, but its aggregate CPU time remains bounded by the quota.
MemoryMax=1G maps to the cgroup v2 hard memory limit. When reclaim cannot bring usage below the boundary, the kernel can invoke the cgroup OOM killer. The process may terminate rather than merely slow down, so use this setting only when that failure mode is acceptable.
To give the scope a recognizable name and keep it attached to the terminal, use:
systemd-run --scope --unit=limited-workload.service
-p CPUQuota=50%
-p MemoryMax=1G
-- ./workload
After starting it, find the resulting unit and its cgroup path with:
systemctl status limited-workload.service
systemctl show limited-workload.service -p ControlGroup -p CPUQuotaPerSecUSec -p MemoryMax
A scope ends when its processes exit. For a repeatable workload, put the settings in a service unit instead.
Apply limits to a persistent systemd service
Create a drop-in so package upgrades do not replace the policy. For a service named example.service:
sudo systemctl edit example.service
Enter a drop-in containing only the controls you need:
[Service]
CPUQuota=50%
CPUWeight=200
MemoryMax=1G
IOReadBandwidthMax=/dev/nvme0n1 20M
TasksMax=256
Reload the unit and restart it:
sudo systemctl daemon-reload
sudo systemctl restart example.service
Each property addresses a different problem:
CPUQuota=50%: a hard bandwidth cap equal to half of one CPU. A value of200%permits two CPUs’ worth of aggregate time, subject to parent limits.CPUWeight=200: proportional priority when competing services are runnable. It favors this service relative to siblings; it does not reserve a fixed amount of CPU and does not stop the service from using idle capacity.MemoryMax=1G: a hard memory ceiling with a possible cgroup OOM event when the limit cannot be met.IOReadBandwidthMax=/dev/nvme0n1 20M: caps read bandwidth for the specified block device. Use the device node that actually backs the workload; a limit on the wrong device has no useful effect.TasksMax=256: limits the number of processes and threads in the unit, helping prevent a fork or thread explosion.
Do not combine a quota and a weight merely because both mention CPU. Use the quota when the requirement is “never exceed this aggregate bandwidth.” Use the weight when the requirement is “receive a larger or smaller share when there is contention.” You can use both when a service needs a ceiling as well as a relative priority below that ceiling.
Hard memory limits versus memory pressure
MemoryMax= and memory.max
MemoryMax=1G in systemd writes the equivalent cgroup v2 hard limit. Memory accounting includes major user-space allocations, page cache, and several kernel structures; the exact set can expand as kernel accounting coverage changes. When usage reaches the boundary, the kernel attempts reclaim. If it cannot reduce usage, a cgroup-scoped OOM action can kill a process in that group.
The boundary is not an instantaneous byte-for-byte fuse. The kernel documentation notes that memory.max can be exceeded temporarily in some circumstances and that not every allocation path invokes the OOM killer. Treat it as a control boundary with an observable failure path, not as a guarantee that every allocation stops at exactly the displayed number.
MemoryHigh= and memory.high
memory.high is a throttling and reclaim boundary. Crossing it applies memory pressure and can slow the cgroup while the kernel reclaims pages; it does not directly invoke the cgroup OOM killer. A common staged policy is to set MemoryHigh=768M to start pressure and MemoryMax=1G as the final failure boundary.
For a service drop-in:
[Service]
MemoryHigh=768M
MemoryMax=1G
Choose the values from the workload’s normal working set and its acceptable degradation point. A high limit that is too close to the hard maximum leaves little room for reclaim; a maximum that is too low causes avoidable OOM kills during legitimate bursts.
Use CPU controls correctly
Quota is time bandwidth
In the kernel interface, cpu.max contains a quota and period. The equivalent systemd property, CPUQuota=, expresses the quota as a percentage of one CPU. CPU quota limits aggregate scheduling time over periods; it does not lock a process to a particular core, lower its frequency, or guarantee a latency target. A throttled service can experience pauses even when its average CPU use appears acceptable.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Weight is proportional sharing
CPUWeight= maps to the cgroup’s proportional CPU weight. It matters only when sibling groups compete for CPU. A higher weight increases the share under contention, while an idle machine may let every group run at full speed. Weight is therefore suitable for prioritization, not for a strict upper bound.
Inspect actual scheduling and throttling data with:
systemctl show example.service -p ControlGroup
cat /sys/fs/cgroup/system.slice/example.service/cpu.stat
The exact path varies by unit and slice. cpu.stat exposes usage and throttling counters that reveal whether a quota is being hit rather than merely configured.
Limit I/O and process creation
Block I/O
Systemd’s IOReadBandwidthMax= and related properties write cgroup I/O policies. The direct v2 interface is io.max. A device-specific policy must name the block device that carries the workload’s traffic. Storage stacks, encryption, and virtual devices can make the apparent device different from the physical medium, so verify the path before relying on the limit.
cat /sys/fs/cgroup/system.slice/example.service/io.stat
io.stat reports per-device activity and is the primary check that the workload is using the cgroup you configured.
Process and thread count
TasksMax=256 maps to pids.max. It counts tasks, including threads, not just traditional process IDs. A service that creates many worker threads can reach the limit even when its process count looks small. Check current usage with:
systemctl show example.service -p TasksCurrent -p TasksMax
cat /sys/fs/cgroup/system.slice/example.service/pids.current
When the limit is reached, new process or thread creation fails. Set a value above the service’s normal peak and test startup paths that create workers dynamically.
Configure cgroup v2 files directly
Use this method only when systemd or another supervisor is not the cgroup manager. The example creates a child group named limited below the current cgroup root. Run it as root or with equivalent delegated permissions.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute- Create the child cgroup:
sudo mkdir /sys/fs/cgroup/limited - Enable the controllers in the parent’s subtree. The controller names must appear in
cgroup.controllers:echo '+cpu +memory +io +pids' | sudo tee /sys/fs/cgroup/cgroup.subtree_controlIf a controller is unavailable, remove it from the command rather than assuming the kernel supports it.
- Set the child’s limits. For CPU,
50000 100000means 50,000 microseconds of CPU time in a 100,000-microsecond period:echo '50000 100000' | sudo tee /sys/fs/cgroup/limited/cpu.max echo '1G' | sudo tee /sys/fs/cgroup/limited/memory.max echo '768M' | sudo tee /sys/fs/cgroup/limited/memory.high echo '256' | sudo tee /sys/fs/cgroup/limited/pids.max - Apply an I/O policy using the device’s major and minor numbers. For example, first obtain them with
lsblk, then write a device-specific rule in the form required byio.max:echo 'MAJOR:MINOR rbps=20971520' | sudo tee /sys/fs/cgroup/limited/io.maxReplace
MAJOR:MINORwith the real values; do not write the literal placeholder. - Move the workload into the group by writing its PID to
cgroup.procs:echo 12345 | sudo tee /sys/fs/cgroup/limited/cgroup.procsMove the process before it creates children if you need those descendants to remain in the same group. A process can be moved only where the hierarchy’s delegation and permissions allow.
The parent must enable a controller before the child receives its interface file. A child also cannot escape limits imposed by ancestors. If a manager owns the parent, configure the manager rather than changing these files underneath it.
Rank #4
Verify limits, usage, and failure events
Configuration files show requested values; event and usage files show behavior. For a service, first obtain its cgroup path:
systemctl show example.service -p ControlGroup
Then inspect the corresponding directory below /sys/fs/cgroup. Useful files include:
cpu.stat: CPU usage, periods, and throttling counters.memory.current: current accounted memory.memory.events: counters such as high-pressure and maximum-limit events, including OOM activity.memory.stat: a breakdown of memory categories.io.stat: per-device I/O usage.pids.current: tasks currently in the group.pids.events: process-count limit events.
For a live overview of systemd units, run:
systemd-cgtop
Compare observed counters with the policy. A rising CPU throttling counter means the quota is actively limiting work. A rising memory.events maximum or OOM counter means the memory boundary is being reached, not merely that the value was written successfully.
Recommended Free Tools
Remove or change a policy safely
Systemd service
Edit the drop-in and remove the property, or reset it explicitly, then reload and restart:
sudo systemctl edit example.service
sudo systemctl daemon-reload
sudo systemctl restart example.service
Stopping a service does not necessarily remove a persistent drop-in; the file remains until you edit it again.
Transient scope
Stop the scope by unit name:
systemctl stop limited-workload.service
The cgroup disappears after its processes exit and systemd releases it.
Direct cgroup
Move or terminate all processes in the child before removing its directory:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →cat /sys/fs/cgroup/limited/cgroup.procs
sudo rmdir /sys/fs/cgroup/limited
A non-empty cgroup cannot be removed. Do not delete a directory belonging to a systemd unit; let systemd manage that lifecycle.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshoot common failures
The control file does not exist
Confirm that the host is using cgroup v2 and that the controller is enabled in the parent’s cgroup.subtree_control. A controller listed nowhere in cgroup.controllers is unavailable to that hierarchy, possibly because of kernel configuration or delegation.
Best Value
- New
- Mint Condition
- Dispatch same day for order received before 12 noon
- Guaranteed packaging
- No quibbles returns
The service ignores the limit
Check the unit’s actual ControlGroup path and inspect that directory’s files. The process may have been started outside the service, placed in a different scope, or moved by a container runtime. Also check ancestor limits: a parent can make the effective allowance smaller than the unit’s configured value.
Memory use is lower than expected, but the process is killed
Look at memory.events and memory.stat. Accounting includes page cache and several kernel structures, not only anonymous heap memory. A burst can also reach the hard boundary before monitoring samples capture the peak. Raise MemoryMax=, add a lower MemoryHigh= for gradual pressure, or reduce the workload’s working set.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsCPU quota causes periodic stalls
Inspect cpu.stat for throttling. A quota is enforced over scheduling periods, so a burst followed by a throttled interval can create latency even when average utilization is modest. If the goal is fairness rather than a ceiling, replace or supplement the quota with an appropriate CPUWeight=.
Containers behave differently from host services
Kubernetes and other container runtimes translate pod or container CPU and memory settings into cgroup policies. Check whether the host uses cgroup v1 or v2, which runtime and cgroup driver are active, and which parent slice contains the container. Host-level ancestor limits can reduce the resources available to a pod, while a runtime may rewrite child settings.
Design a policy that matches the failure you want
| Requirement | Use | What happens at contention |
|---|---|---|
| Never allow more than a defined CPU bandwidth | CPUQuota= or cpu.max |
The group is throttled after consuming its period allowance |
| Give one service priority over siblings | CPUWeight= or cpu.weight |
Relative share changes only while groups compete |
| Slow memory growth before an emergency | MemoryHigh= or memory.high |
Reclaim pressure and throttling increase; no direct cgroup OOM action |
| Set a final memory boundary | MemoryMax= or memory.max |
Reclaim is attempted; a cgroup OOM kill can follow |
| Prevent fork or thread storms | TasksMax= or pids.max |
New task creation fails at the limit |
| Restrict storage throughput | IOReadBandwidthMax= or io.max |
I/O is limited for the selected device and direction |
Start with a measured baseline, apply one control at a time, and watch the relevant event counters during normal traffic and deliberate stress. Keep enough headroom for startup, cache warming, and short-lived bursts. Record the unit or cgroup path alongside the policy so an operator can verify that the intended processes are covered.
Relationship to containers and Linux versions
Container isolation is not separate from cgroups: runtimes use cgroups to enforce CPU, memory, process-count, and I/O settings for containers. A pod limit therefore depends on the host kernel’s controller support, hierarchy mode, delegation, and runtime configuration. When a container appears to exceed or fall short of its requested resources, inspect the host cgroup mode and the runtime’s selected driver before changing application settings.
The Linux kernel man-pages project identifies cgroups v2 as official beginning with Linux 4.5; the exact controls available on a current machine still depend on its kernel configuration and manager. systemd has exposed interfaces for creating and managing labeled process groups since version 205, and modern distributions generally present those interfaces through unit properties rather than requiring applications to write kernel files themselves.
Practical checklist
- Confirm
stat -fc %T /sys/fs/cgroupreportscgroup2fs. - Check
cgroup.controllersand enable only controllers that are available and delegated. - Identify the process’s real unit or cgroup path before writing a policy.
- Use a transient
systemd-run --scopefor experiments and a service drop-in for persistent daemons. - Choose quota for a ceiling, weight for relative priority,
memory.highfor pressure, andmemory.maxfor a hard boundary. - Verify
cpu.stat,memory.current,memory.events,io.stat, andpids.currentafter deployment. - Check ancestor groups and container runtime settings whenever effective behavior differs from the unit’s configured value.
Frequently Asked Questions
Does CPUQuota=50% limit a process to half of a CPU core?
It limits aggregate CPU time to the equivalent of half of one CPU over scheduling periods. It does not pin the process to a core or guarantee latency, and short bursts can still occur before throttling.
Will MemoryMax= immediately kill a process when it reaches the displayed number?
No. The kernel first attempts reclaim, and the boundary can be exceeded temporarily in some circumstances. If usage cannot be reduced, a cgroup OOM action may kill a process in the group.
Can I manage the same cgroup with systemd and manual echo commands?
Do not do so. A cgroup v2 group should have one manager. Configure a systemd-owned group through unit properties, or use direct files only in a hierarchy you control and have delegated.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
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.

