Alright, let’s get our hands dirty with cgroups. If namespaces are about providing isolation (making you think you’re alone), cgroups are the prison guards enforcing the rules. They’re about resource accounting and limiting. They answer the crucial question: “How much CPU, memory, and I/O can this process, and all its future children, actually use?”

You’ll run into two flavors: the old, slightly dysfunctional v1 and the newer, more coherent v2. The Linux kernel maintainers looked at the glorious mess of v1 and said, “We can do better.” And they did. v2 is a significant redesign, not just an incremental update. The key difference is philosophical: v1 let you control different resources (CPU, memory, I/O) with multiple, independent hierarchies. v2 enforces a single, unified hierarchy. This sounds boring, but it’s the difference between herding a dozen cats and commanding a well-drilled squad of soldiers. v1 was the cats.

The V1 Zoo: A Confusion of Controllers

In v1, each “controller” (like cpu, memory, blkio) could be mounted on its own virtual filesystem, and you could create entirely separate organizational structures for each. You could have a cgroup for “high-cpu” processes and another for “low-memory” processes, and a single process could be in both. This was flexible in theory but a nightmare to reason about in practice.

Let’s say you wanted to limit the memory of a process. First, you’d find or create a cgroup via the memory controller.

# Mount the memory controller (if not already mounted)
sudo mkdir -p /sys/fs/cgroup/memory
sudo mount -t cgroup -o memory cgroup /sys/fs/cgroup/memory

# Create a new cgroup called 'myapp'
sudo mkdir /sys/fs/cgroup/memory/myapp

# Set a limit of 100MB
echo "100M" | sudo tee /sys/fs/cgroup/memory/myapp/memory.limit_in_bytes

# Now, add a process to it. This is the magic.
echo $$ | sudo tee /sys/fs/cgroup/memory/myapp/tasks

Boom. Your current shell is now limited to 100MB of RAM. Try to allocate more, and it will get killed by the infamous OOM (Out-Of-Memory) killer. The problem? You now have to do this again for the cpu controller, and again for blkio, each with its own maze of files in /sys/fs/cgroup/. It worked, but it felt like duct tape and baling wire.

The V2 Unification: One Hierarchy to Rule Them All

cgroups v2 said, “Enough.” There is now one hierarchy, and all controllers are enabled together on a single filesystem mount. This is not just cleaner; it eliminates the weird edge cases from v1 where a process in one cgroup for memory and another for CPU could create conflicting policies.

On a modern system, you’ll probably see v2 already mounted:

mount | grep cgroup2
# cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,seclabel)

The structure under /sys/fs/cgroup/ is now the one true hierarchy. You create a subdirectory, and all controllers apply to it.

# Create a cgroup for our test app
sudo mkdir /sys/fs/cgroup/myapp_v2/

# Let's limit memory to 200MB and CPU usage to half a core (500ms per second)
echo "200000000" | sudo tee /sys/fs/cgroup/myapp_v2/memory.max
echo "50000 100000" | sudo tee /sys/fs/cgroup/myapp_v2/cpu.max
# The format for cpu.max is: $MAX $PERIOD
# This means the process can use $MAX microseconds out of every $PERIOD microsecond window.
# So 50,000 us out of every 100,000 us = 50% of a CPU core.

# Add your shell to it
echo $$ | sudo tee /sys/fs/cgroup/myapp_v2/cgroup.procs

See the difference? All the control files are in one place. The interface is more consistent (memory.max vs the v1 memory.limit_in_bytes). It’s just… saner.

Why You Should (Probably) Use V2

Unless you’re trapped on an ancient kernel (pre-4.5) or using a piece of software that hasn’t caught up yet (looking at you, some older container runtimes), you should be using cgroups v2. The unified hierarchy provides more predictable behavior, better delegation of resources, and new features like pressure stall information (PSI)—a fantastic way to know if your processes are starving for resources—that are only available in v2.

The biggest pitfall? The transition. Many older tutorials and tools are built for v1. If you see instructions messing with files in /sys/fs/cgroup/cpu/ or /sys/fs/cgroup/memory/, you’re dealing with v1. The other gotcha is that you can’t have both v1 and v2 controllers active for the same resource. The kernel has a boot parameter, cgroup_no_v1=, to disable v1 controllers entirely, which is a strong hint about where the future lies. My advice? Learn v2. It’s the present and the future, and it’s a much less absurd way to manage your resources.