Configuring cgroup v2 memory.high and memory.max to Prevent OOM Kills
Set memory.high below memory.max in cgroup v2 to create a reclaim buffer that throttles workloads before the OOM killer triggers. Learn the mechanism, a worked configuration, and how to verify reclamation behavior.
24 Jan 2026, 15:43 UTC

When a Linux workload exceeds its memory allocation, the kernel's default reaction is blunt: the OOM killer terminates a process to protect the host. cgroup v2 offers a finer control through memory.high and memory.max. The useful takeaway is that memory.high acts as a soft ceiling that forces reclamation and throttles allocation, while memory.max is the hard boundary that triggers the OOM killer. Setting memory.high meaningfully below memory.max gives a workload room to burst, but pushes it to release memory before it hits the fatal limit.
How the Kernel Enforces the Two Limits
In the cgroup v2 unified hierarchy, typically mounted at /sys/fs/cgroup, the memory controller must be enabled in the parent group before child groups expose the memory.* interface files. Once enabled, memory.max is a hard cap: if the group's current usage reaches this value and the kernel cannot reclaim enough, it invokes the OOM killer inside that cgroup. memory.high is a throttle. When usage crosses this threshold, the kernel does not kill the process; instead, it forces the allocating task into synchronous reclamation, cleaning page cache or swapping anonymous pages before the allocation succeeds. This reclaim happens in the context of the process that triggered the allocation, which directly slows that workload.
Worked Configuration: Creating a Reclaim Buffer
The following example creates a cgroup with a 500 MiB soft limit and a 1 GiB hard limit. Run these commands as root on the host. Replace <group> with your application name and <pid> with the target process ID.
# Ensure the memory controller is available in the root cgroup cat /sys/fs/cgroup/cgroup.controllers # If 'memory' is missing, enable it for child groups echo "+memory" > /sys/fs/cgroup/cgroup.subtree_control # Create the child cgroup mkdir -p /sys/fs/cgroup/<group> # Soft limit: 500 MiB (524288000 bytes) echo 524288000 > /sys/fs/cgroup/<group>/memory.high # Hard limit: 1 GiB (1073741824 bytes) echo 1073741824 > /sys/fs/cgroup/<group>/memory.max # Move the process into the cgroup echo <pid> > /sys/fs/cgroup/<group>/cgroup.procs
After the process is moved, monitor memory.current to see usage. When usage exceeds 500 MiB, the kernel begins reclaiming and throttling. If the process continues allocating and reaches 1 GiB, reclamation is no longer sufficient and the OOM killer selects a victim in that cgroup.
Verifying Reclamation and Throttling
To confirm the limits are working, check the event counters and pressure information.
# See how many times each limit was hit cat /sys/fs/cgroup/<group>/memory.events # Observe stall time caused by reclaim cat /sys/fs/cgroup/<group>/memory.pressure
The memory.events file reports high and max counters. A rising high count means the soft limit is being enforced. A rising max count or oom_kill means the hard limit was breached. The memory.pressure file shows stall time for memory reclaim, which helps quantify the latency cost of the soft limit.
Limits, Risks, and Common Mistakes
memory.high does not guarantee that usage stays below the threshold. If the kernel cannot reclaim pages quickly enough, the cgroup will sail past memory.high and hit memory.max, resulting in an OOM kill. This is especially likely when the gap between the two values is too small or when the workload allocates memory faster than reclaim can free it.
A second mistake is setting memory.high too close to steady-state usage. If the value sits just above the working set, the kernel will constantly reclaim and the workload will immediately refill the page cache, causing thrashing and sustained latency. Choose a memory.high that accommodates the normal working set plus a reasonable burst margin.
Memory that is pinned with mlock(2) or similar mechanisms cannot be reclaimed. If a significant portion of the cgroup's usage is locked, memory.high throttling will not free enough space, and the process will stall until it reaches memory.max.
Finally, remember that these controls only exist in cgroup v2. If your system still uses the v1 hierarchy, the memory.* interface files will not appear. Verify the version by checking for /sys/fs/cgroup/cgroup.controllers. On systemd hosts, you can also express the same limits declaratively with MemoryHigh= and MemoryMax= in the unit file, which writes to the same kernel interface.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.