containerd’s systemd cgroup driver is the default in many modern Linux distributions, but it’s often confused with the older cgroupfs driver, leading to subtle misconfigurations.
Let’s see what this looks like in practice. We’ll set up a simple container and then observe how systemd manages its cgroups.
First, ensure containerd is running and configured to use systemd. The config.toml file, typically located at /etc/containerd/config.toml, is where this is set. Look for the [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] section. You want SystemdCgroup = true.
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
If this is set to false or is commented out, you’ll need to edit the file, restart containerd (sudo systemctl restart containerd), and then create a new container.
Now, let’s run a simple Nginx container:
sudo ctr run --rm docker.io/library/nginx:latest my-nginx
Once it’s running, we can inspect its cgroup. Because we’ve configured containerd to use the systemd driver, systemd itself will be managing the cgroup hierarchy for this container. The cgroup path will reflect this.
To find the cgroup, we can use systemctl status on the container’s systemd unit. containerd, when using the systemd driver, creates a systemd service unit for each container. The unit name is usually prefixed with containerd-.
First, find the container ID:
sudo ctr containers list
Let’s say your container ID is my-nginx-container-id. The systemd unit name would be containerd-my-nginx-container-id.scope.
Now, check the status of this unit:
sudo systemctl status containerd-my-nginx-container-id.scope
The output will show you the cgroup path associated with this unit. It will look something like this:
...
Main PID: 12345 (nginx)
CGroup: /system.slice/containerd-my-nginx-container-id.scope
└─12345 /usr/sbin/nginx -g daemon off;
...
Notice the CGroup: line. It points to a path under /system.slice/. This is the key indicator that systemd is managing the cgroup. If you were using the cgroupfs driver, the path would typically be under /sys/fs/cgroup/systemd/ or a similar path directly managed by the container runtime.
The problem this solves is that systemd, as the init system, is designed to manage processes and their resources. By using the systemd cgroup driver, containerd delegates this responsibility to systemd. This allows systemd to enforce its own resource controls, logging, and lifecycle management on containers, integrating them more seamlessly into the host’s system management. It means you can use standard systemd tools to inspect and manage container resources.
The exact levers you control are through systemd’s unit files. When containerd creates the .scope unit for a container, it can pass configuration that systemd interprets. For example, you can set CPU limits, memory limits, and other resource controls directly in the containerd configuration that translate into systemd unit directives.
The config.toml file is the primary place to control this. Under [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options], you’ll find SystemdCgroup = true. This flag tells containerd to use systemd’s cgroup driver. If this is false, containerd will use the cgroupfs driver, which means it directly manipulates the cgroup filesystem entries under /sys/fs/cgroup/.
The actual cgroup path for a container running with the systemd driver will always be a descendant of a systemd service or scope unit, typically within /system.slice/ or /user.slice/. For example, a container might end up in a path like /system.slice/containerd-abcdef123456.scope/.
The most surprising true thing about this is that while containerd is configured to use the systemd cgroup driver, the container process itself might not directly be a child of the systemd unit. Systemd manages the cgroup of the container process, which is a level of indirection. containerd launches a shim process, and systemd manages the cgroup for that shim, and the container process inherits from that shim’s cgroup.
If you’ve correctly set SystemdCgroup = true in config.toml and restarted containerd, but you still see cgroup paths under /sys/fs/cgroup/docker/ or similar, it’s likely that the runc runtime within containerd hasn’t been reconfigured. You might need to ensure the runc binary itself is aware of or configured to respect the systemd driver setting, or that containerd is passing the correct parameters to runc to trigger systemd cgroup management.
After fixing the SystemdCgroup setting and restarting containerd, you might encounter issues with resource limits not being applied as expected, which often points to how those limits are specified within the containerd configuration itself, rather than the driver choice.