Understanding Control Groups (cgroups)
In enterprise Linux environments, a single server often hosts multiple critical applications—for instance, a MySQL database and a Python web application. If the Python application encounters a bug and suddenly consumes 100% of the CPU or leaks gigabytes of memory, the MySQL database will be starved of resources, causing the entire server to crash.
Linux Control Groups (cgroups) are a kernel feature that limits, accounts for, and isolates the resource usage (CPU, memory, disk I/O) of a collection of processes. While Docker and Kubernetes rely heavily on cgroups under the hood, system administrators can manually configure cgroups (specifically cgroups v2) via Systemd to enforce strict hardware limits on bare-metal or virtualized applications.
Prerequisites
Ensure your Linux distribution uses cgroups v2. Ubuntu 22.04 and 24.04 use v2 by default. You can verify this by running:
mount | grep cgroup
If you see cgroup2 on /sys/fs/cgroup, your system is using the modern cgroups v2 architecture.
Step 1: Identifying the Target Service
cgroups in modern Linux are intimately tied to Systemd. Every Systemd service automatically runs within its own cgroup slice. To limit an application, you must apply limits directly to its Systemd service file.
In this example, we will assume you have a runaway background service named data-cruncher.service that occasionally consumes too much memory, leading to Out Of Memory (OOM) kernel panics.
Step 2: Applying Memory Limits via Systemd Drop-ins
You should not edit the main service file located in /lib/systemd/system/. Instead, you should create a drop-in configuration snippet that overrides specific settings.
Use the systemctl edit command to safely create a drop-in file:
sudo systemctl edit data-cruncher.service
This opens a blank file in your default text editor. Add the following lines to enforce a strict memory limit of 500 Megabytes:
[Service]
MemoryMax=500M
MemoryHigh=400M
- MemoryMax: This is a hard limit. If the application attempts to allocate 501MB, the Linux kernel’s OOM killer will instantly terminate the process.
- MemoryHigh: This is a soft limit. When memory usage hits 400MB, the kernel aggressively throttles the process and forces it to reclaim memory, slowing it down before it reaches the fatal
MemoryMaxlimit.
Save the file and exit the editor.
Step 3: Applying CPU Limits
You can also constrain CPU usage to prevent the application from monopolizing the processor. Open the drop-in file again (sudo systemctl edit data-cruncher.service) and add CPU limits:
[Service]
MemoryMax=500M
MemoryHigh=400M
CPUQuota=50%
The CPUQuota=50% setting guarantees that the application can never consume more than half of a single CPU core, leaving the rest of the processor free for other critical databases or web servers.
Step 4: Reloading and Verifying cgroups
After saving the drop-in file, you must reload the Systemd daemon to parse the new configuration, and then restart the service to apply the cgroup boundaries.
sudo systemctl daemon-reload
sudo systemctl restart data-cruncher.service
To verify that the kernel has actively applied the limits, you can inspect the raw cgroup virtual filesystem. Find the cgroup path for your service:
systemctl status data-cruncher.service | grep CGroup
Navigate to that path (e.g., /sys/fs/cgroup/system.slice/data-cruncher.service) and read the memory.max file:
cat /sys/fs/cgroup/system.slice/data-cruncher.service/memory.max
It should output the byte equivalent of 500MB (524288000), confirming that the Linux kernel is actively policing the process.
Conclusion
Understanding and utilizing cgroups v2 via Systemd allows Linux administrators to build highly resilient servers. By placing strict resource cages around unstable or resource-hungry applications, you ensure system stability without requiring complex containerization platforms.