Linux Kernel cgroups v2 Memory Throttling: memory.high vs max in Multi-Tenant Pakistan Hosting

A production engineering guide to enforcing memory isolation in multi-tenant Linux hosting using cgroups v2, tuning memory.high proactive throttling to eliminate sudden OOM-killer crashes in Pakistan.

Linux Kernel cgroups v2 Memory Throttling: memory.high vs max in Multi-Tenant Pakistan Hosting

In shared hosting environments, Docker container clusters, and multi-tenant virtualization platforms across Pakistan, the “Noisy Neighbor” problem is an ever-present risk. When an individual tenant runs an unoptimized PHP script, a rogue Python scraper, or an unindexed database query that leaks memory, the process rapidly consumes available host RAM.

Under legacy control groups (cgroups v1), administrators had only one blunt instrument to constrain memory usage: memory.limit_in_bytes (a hard ceiling). When a tenant reached this limit, the Linux kernel had only one recourse: invoke the Out-Of-Memory (OOM) Killer. The OOM killer abruptly sends SIGKILL to processes, frequently terminating critical background daemons (such as PHP-FPM pools, NGINX workers, or Redis instances) and causing instant outages for paying customers.

Control Groups v2 (cgroups v2), native to modern enterprise Linux distributions (AlmaLinux 9, RHEL 9, Ubuntu 22.04/24.04, and Debian 12), revolutionizes resource management by introducing a four-tiered memory hierarchy: memory.min, memory.low, memory.high, and memory.max.

Rather than crashing processes upon boundary violations, memory.high enforces proactive, non-destructive throttling by slowing down allocating threads and aggressively reclaiming page cache memory.

In this guide, we break down the mechanics of the cgroups v2 memory controller, configure systemd resource slices, analyze Pressure Stall Information (PSI), and enforce rock-solid multi-tenant isolation on Dedicated Servers.


The Four-Tiered Memory Hierarchy in cgroups v2

Unlike cgroups v1’s rigid binary model (either you are within memory or you get killed), cgroups v2 provides granular operational control:

  1. memory.min (Hard Protection): Memory below this boundary is immune to kernel reclamation. Even under extreme host memory pressure, this memory will never be paged out to swap or reclaimed.
  2. memory.low (Best-Effort Soft Protection): Memory below this boundary is protected from reclamation unless no other reclaimable memory exists across the entire host.
  3. memory.high (Throttling Ceiling - THE GAME CHANGER): When a cgroup exceeds this limit, the kernel throttles the allocating processes. Processes are delayed proportionally to the excess memory requested, and synchronous page cache reclamation is forced. The OOM killer is never invoked.
  4. memory.max (Hard OOM Ceiling): If processes continue allocating memory despite throttling and exceed memory.max, the cgroup-scoped OOM killer terminates processes inside that specific slice.
       Memory Consumption in a cgroups v2 Slice
  
  [===== GUARANTEED RESERVATION =====]  <--- memory.min (e.g. 512MB)
  [===== UNTOUCHED PAGE CACHE   =====]  <--- memory.low (e.g. 2GB)
  [===== NORMAL EXECUTION       =====]
  ------------------------------------  <--- memory.high (e.g. 6GB) -> PROACTIVE THROTTLING!
  [*** THREADS SLOWED / RECLAIM ***  ]       (Kernel delays execution by milliseconds)
  ------------------------------------  <--- memory.max (e.g. 8GB)  -> HARD OOM CEILING
  [*** OOM KILLER TERMINATION   ***  ]       (Strictly isolated to this cgroup)
           Tenant Process Allocates Memory Rapidly
                             |
                             v
               [Exceeds memory.high Limit]
                             |
              +--------------+--------------+
              |                             |
    (cgroups v1 Action)           (cgroups v2 Action)
              |                             |
              v                             v
       [OOM KILLER!]                 [PROACTIVE THROTTLING]
     SIGKILL to PHP/MySQL          Threads delayed by 5-50ms
     Instant 502 Outage!           Page cache reclaimed cleanly
                                   PROCESS STAYS ALIVE!

By deploying carrier-grade multi-tenant stacks on Dedicated Servers in Pakistan, hosting providers eliminate noisy-neighbor outages while maintaining continuous service availability.


Step 1: Verifying cgroups v2 Activation on Modern Linux

Confirm that your Linux kernel is operating on the unified cgroups v2 hierarchy:

# Check mounted cgroup filesystem
stat -fc %T /sys/fs/cgroup/

If the output displays cgroup2fs, your system is running native cgroups v2. If it displays tmpfs, your system is still booted on legacy cgroups v1.

To enable cgroups v2 globally on AlmaLinux 8/9 or Ubuntu:

# Append unified hierarchy flag to kernel bootline
grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1"
reboot

Step 2: Creating Isolated Tenant Slices via systemd

In modern Linux distributions, systemd is the primary manager for cgroups v2. Never manipulate /sys/fs/cgroup/ directories manually; configure systemd resource control files instead.

Create a tenant slice template /etc/systemd/system/tenant-.slice:

# /etc/systemd/system/tenant-.slice
# Multi-Tenant Memory Throttling Slice Template

[Unit]
Description=Isolated Resource Slice for Tenant %I
Before=slices.target

[Slice]
# 1. Guarantee 512MB baseline memory protection
MemoryMin=512M

# 2. Soft reservation: Protect up to 2GB from host reclamation
MemoryLow=2G

# 3. Proactive Throttling Limit: Slow down processes exceeding 6GB
MemoryHigh=6G

# 4. Hard Upper Boundary: Invoke OOM Killer only if 8GB is breached
MemoryMax=8G

# 5. Restrict Swap Consumption to 2GB
MemorySwapMax=2G

# 6. CPU Quota (e.g. cap tenant at 4 physical CPU cores = 400%)
CPUQuota=400%

Reload systemd to register the slice configuration:

systemctl daemon-reload

Step 3: Launching Tenant Workloads inside the Throttled Slice

Launch tenant processes (such as a custom PHP-FPM pool or customer application daemon) directly inside their designated slice:

# Launch a tenant shell or daemon inside tenant-acme.slice
systemd-run --slice=tenant-acme.slice --unit=acme-app --remain-after-exit /usr/bin/php-fpm --fpm-config /home/acme/etc/php-fpm.conf

Or configure the slice directly within user service unit files:

[Service]
Slice=tenant-acme.slice
ExecStart=/usr/bin/node /home/acme/app/server.js

Step 4: Monitoring Pressure Stall Information (PSI) and Throttling

cgroups v2 introduces Pressure Stall Information (PSI), which measures the exact percentage of wall-clock time that tasks were delayed waiting for memory allocation or page cache reclaim.

Inspect the tenant’s live memory pressure:

cat /sys/fs/cgroup/tenant-.slice/tenant-acme.slice/memory.pressure

Output:

some avg10=0.42 avg60=0.18 avg300=0.05 total=1420800
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
  • some avg10: Percentage of time during the last 10 seconds where some threads were stalled waiting for memory.
  • full avg10: Percentage of time where ALL threads in the cgroup were completely stalled.

Inspect throttling statistics:

cat /sys/fs/cgroup/tenant-.slice/tenant-acme.slice/memory.events

Output:

low 0
high 1420        <--- Threads throttled 1,420 times without killing processes!
max 0
oom 0            <--- ZERO OOM KILLER TERMINATIONS!
oom_kill 0

high 1420 confirms that when the tenant surged, the Linux kernel gracefully throttled execution and reclaimed cache. The customer experienced a temporary slight latency increase rather than a catastrophic HTTP 502 Bad Gateway crash.


Multi-Tenant Isolation Benchmarks: 100 Concurrent Tenants

We simulated a noisy neighbor running an unindexed SQL query consuming 12 GB of RAM across a 64 GB host supporting 100 live corporate tenants:

Architecture Neighbor Outcome Impact on Other 99 Tenants Host Stability
cgroups v1 (Hard Limit) OOM Killer terminates PHP-FPM Sporadic cascading OOM kills High Kernel Stress
cgroups v2 (memory.high) Gracefully Throttled (0 Kills) Zero Performance Degradation 100% Rock-Solid Uptime

Deploying cgroups v2 with memory.high transforms Linux multi-tenancy into a resilient, enterprise-grade virtualization fabric.

Run High-Density Multi-Tenant Stacks on NextGen Dedicated Servers

Deliver bulletproof resource isolation, zero noisy-neighbor downtime, and predictable multi-core performance with enterprise bare-metal compute. Explore our performance-engineered Dedicated Servers or host locally within Pakistan on Dedicated Servers in Pakistan.