Linux Control Groups (cgroups V2): Limiting CPU And Memory For Production Workloads
Linux Control Groups (cgroups v2): CPU & Memory Limits — Quick Reference
If you're running production workloads without cgroup constraints, you're one runaway process away from a bad day. Here's everything you need to configure cgroups v2 limits fast.
Verify You're on cgroups v2
# Check cgroup version
stat -fc %T /sys/fs/cgroup/
# Returns "cgroup2fs" = v2, "tmpfs" = v1
# Or check mount
mount | grep cgroup
If you're on a modern kernel (5.8+) with systemd 248+, you're almost certainly on v2. Good.
CPU Limits
Key Files
| File | Purpose |
|---|---|
cpu.max | Hard CPU quota (period-based) |
cpu.weight | Relative CPU priority (default: 100) |
cpu.pressure | PSI metrics for CPU saturation |
Set a CPU Quota (Hard Limit)
# Format: "quota period" (in microseconds)
# Limit to 50% of one CPU core:
echo "50000 100000" > /sys/fs/cgroup/myapp/cpu.max
# Limit to 2 full CPU cores:
echo "200000 100000" > /sys/fs/cgroup/myapp/cpu.max
# Remove limit entirely:
echo "max 100000" > /sys/fs/cgroup/myapp/cpu.max
Set CPU Weight (Soft Limit / Priority)
# Range: 1-10000, default is 100
# Give this cgroup 2x priority over defaults:
echo "200" > /sys/fs/cgroup/myapp/cpu.weight
# Deprioritize a batch job:
echo "10" > /sys/fs/cgroup/myapp/cpu.weight
Memory Limits
Key Files
| File | Purpose |
|---|---|
memory.max | Hard limit — OOM kill above this |
memory.high | Soft limit — throttle before OOM |
memory.min | Guaranteed minimum (never reclaimed) |
memory.current | Current usage (read-only) |
memory.events | OOM event counters |
Set Memory Limits
# Hard limit at 512MB (OOM kill if exceeded):
echo "536870912" > /sys/fs/cgroup/myapp/memory.max
# Or use human-readable via systemd (see below)
# Soft limit at 400MB (throttle, swap before OOM):
echo "419430400" > /sys/fs/cgroup/myapp/memory.high
# Guarantee 128MB is never reclaimed from this cgroup:
echo "134217728" > /sys/fs/cgroup/myapp/memory.min
Monitor Memory Usage
# Current memory consumption:
cat /sys/fs/cgroup/myapp/memory.current
# Check if OOM events have occurred:
cat /sys/fs/cgroup/myapp/memory.events
# Look for: oom_kill > 0 = you have a problem
# Detailed memory breakdown:
cat /sys/fs/cgroup/myapp/memory.stat
Create and Manage a cgroup
# Create a new cgroup
mkdir /sys/fs/cgroup/myapp
# Add a process (PID) to the cgroup
echo <PID> > /sys/fs/cgroup/myapp/cgroup.procs
# List all processes in the cgroup
cat /sys/fs/cgroup/myapp/cgroup.procs
# Run a command directly in a cgroup (systemd-run):
systemd-run --scope -p CPUQuota=50% -p MemoryMax=512M mycommand
# Delete a cgroup (must be empty first)
rmdir /sys/fs/cgroup/myapp
systemd Slice/Service Limits (The Right Way in Production)
Direct filesystem manipulation doesn't survive reboots. Use systemd for persistent configs.
# /etc/systemd/system/myapp.service
[Service]
CPUQuota=150% # 1.5 cores
CPUWeight=200 # 2x default priority
MemoryMax=1G # Hard limit
MemoryHigh=768M # Soft limit / throttle point
MemoryMin=256M # Guaranteed minimum
MemorySwapMax=0 # Disable swap for this unit
# Apply changes without restart:
systemctl daemon-reload
systemctl restart myapp
# Check effective limits:
systemctl show myapp | grep -E "Memory|CPU"
# Real-time cgroup resource usage:
systemd-cgtop
Pressure Stall Information (PSI) — Don't Ignore This
PSI is cgroups v2's killer feature. It tells you how much a resource is contended.
# CPU pressure for a cgroup:
cat /sys/fs/cgroup/myapp/cpu.pressure
# some avg10=0.00 avg60=0.23 avg300=0.15 total=12345
# Memory pressure:
cat /sys/fs/cgroup/myapp/memory.pressure
# "some" = at least one task was stalled
# "full" = ALL tasks were stalled (critical)
# avg10/60/300 = 10s, 60s, 5min rolling averages
Rule of thumb:
full avg60 > 5%on memory pressure means you need to increase the memory limit or fix a leak.
Quick Troubleshooting Commands
# Find which cgroup a process belongs to:
cat /proc/<PID>/cgroup
# View cgroup hierarchy:
systemd-cgls
# Check kernel OOM kill logs:
dmesg | grep -i "oom\|killed process"
# Watch live resource usage across all cgroups:
systemd-cgtop -d 1
# Verify cgroup controllers are available:
cat /sys/fs/cgroup/cgroup.controllers
# Should show: cpuset cpu io memory hugetlb pids rdma
The Numbers That Matter
| Scenario | Recommended Setting |
|---|---|
| Latency-sensitive API | MemoryHigh = 80% of MemoryMax |
| Batch/background job | CPUWeight=10, MemorySwapMax=unlimited |
| Multi-tenant node | Always set both memory.max AND memory.high |
| No swap tolerance | MemorySwapMax=0 explicitly |
Bottom line: Set memory.high as your early-warning tripwire and memory.max as the hard stop. For CPU, prefer cpu.weight for general workloads and reserve cpu.max for strict SLA enforcement. And watch your PSI metrics — they'll tell you when limits are causing real pain before your users do.
Was this article helpful?
Senior Kubernetes Architect
10+ years orchestrating containers in production. Battle-tested opinions on everything from pod scheduling to service mesh. I've seen clusters burn and helped rebuild them better.
Related Articles
Linux User & Group Management: From Root to Least Privilege
Create and manage Linux users and groups, configure sudo access, understand /etc/passwd and /etc/shadow, and implement PAM authentication policies.
Linux Networking Commands: Cheat Sheet
Linux networking commands cheat sheet for troubleshooting — interfaces, routing, DNS lookups, connections, iptables firewalls, and tcpdump packet capture.
Linux Fundamentals: File System Navigation and Permissions
Navigate the Linux file system hierarchy, master essential commands, understand file permissions and ownership, and work with links, pipes, and redirection.
Linux Networking & Firewall: Configuration and Troubleshooting
Configure Linux network interfaces, set up iptables/nftables/firewalld rules, troubleshoot connectivity with ss, ip, dig, and tcpdump, and secure your servers.
Linux Package Management: apt, dnf, and zypper Compared
Install, update, and manage packages across Ubuntu (apt), RHEL (dnf/yum), and SUSE (zypper) — repositories, GPG keys, version pinning, and automation.
Linux Performance Troubleshooting: CPU, Memory, Disk, and Network
Diagnose Linux performance bottlenecks with top, htop, vmstat, iostat, sar, iotop, and strace — a systematic approach to finding and fixing issues.
More in Linux
View all →Alpine Linux apk Reference: Package Management, Repositories, and System Administration
A complete reference for Alpine Linux's apk package manager — adding packages, managing repositories, pinning versions, building custom packages, and running Alpine as a minimal server OS.
Alpine Linux for Docker: Building Minimal, Secure Container Images
How to use Alpine Linux as a Docker base image effectively — understanding musl libc compatibility, multi-stage builds, layer optimization, and the security trade-offs versus glibc-based distributions.
openSUSE MicroOS and Transactional Updates: Atomic System Updates with Btrfs Snapshots
How openSUSE's transactional-update and Btrfs snapshot integration enables atomic, rollback-safe system updates — and how MicroOS takes this to a fully immutable, container-optimized OS.
openSUSE and SUSE Linux Enterprise: YaST Administration and zypper Package Management
A practical guide to SUSE Linux administration — using YaST for system configuration, zypper for package management, managing repositories, and understanding the differences between openSUSE Leap, Tumbleweed, and SUSE Linux Enterprise.