October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Limit Process Resources with cgroups on Linux (cgroup v2)

A practical cgroup v2 guide for limiting CPU, memory, I/O, and process counts on Linux, with systemd commands, direct kernel-file examples, verification steps, and container caveats.
Length10 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
systemd-run --scope -p CPUQuota=50% -p MemoryMax=1G -- ./workload
  1. 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 of 200% permits up to two CPUs’ worth of time, subject to ancestor limits and available capacity.
  2. Observe the scope from another terminal. Replace the generated name in systemctl status run-*.scope, or list active scopes with systemctl list-units --type=scope.
  3. Stop it cleanly when finished. Use systemctl stop run-...scope if 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Understand the other unit properties

  • MemoryMax=1G sets the hard memory limit for the unit and its descendants.
  • TasksMax=256 limits 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 20M limits 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.

  1. 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_control

    If 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.

  2. 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.max

    cpu.max takes a quota and period in microseconds. The pair above permits 50,000 microseconds of CPU time in each 100,000-microsecond period. Writing max 100000 removes the quota while retaining the period. memory.high is a reclaim and throttling boundary; memory.max is the hard boundary.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  3. 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.

  1. Launch or move the process.
    ./workload &
    PID=$!
    echo $PID | sudo tee $CG/cgroup.procs
    wait $PID

    Writing a PID to cgroup.procs moves 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.

  2. 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.stat includes consumed CPU time and throttling counters such as nr_throttled and throttled_usec. Rising throttling counters mean the quota is being reached, not that the process is bound to a core.
  • memory.current reports current accounted usage. memory.events records pressure and limit events, including high-boundary activity and maximum-limit or OOM-related events.
  • io.stat reports per-device I/O activity and lets you compare the device you limited with the device the workload actually uses.
  • pids.current shows the current process and thread count against pids.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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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
Sale
UNIX and Linux System Administration Handbook, 4th Edition
  • 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • 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, and memory.events where 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

  1. Confirm cgroup v2 and identify the manager that owns the target hierarchy.
  2. Choose a scope for a one-off command, a service unit for a daemon, or a delegated subtree for direct control.
  3. Decide whether each resource needs a hard cap, throttling boundary, proportional weight, or protection from peers.
  4. Set CPU, memory, I/O, and task limits with explicit units and the correct device identifiers.
  5. Start the workload inside the cgroup and verify its PID membership.
  6. Watch usage, pressure, and event counters while the workload is busy.
  7. Adjust the policy based on observed throttling or reclaim behavior, preserving headroom for accounting and service overhead.
  8. 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.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.