Docker Error Guide: 'controller not available' cgroup v2 Resource Limits
Fix Docker's 'failed to write ... cgroup ... controller not available' error on cgroup v2 hosts: enable delegated cpu/memory controllers under systemd and set limits.
- #docker
- #troubleshooting
- #errors
- #cgroups
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Exact Error Message
docker: Error response from daemon: failed to create task for container:
failed to create shim task: OCI runtime create failed: runc create failed:
unable to start container process: error during container init:
error setting cgroup config for procHooks process:
failed to write "100000": write /sys/fs/cgroup/system.slice/docker-<id>.scope/cpu.max:
no such file or directory: unknown
On some hosts the daemon reports it more directly as cgroup: cpu controller not available or Your kernel does not support cgroup memory limit while emitting a WARNING: No cpu cgroup / No memory limit support line at docker run time.
What It Means
On a cgroup v2 (unified hierarchy) host, resource controllers like cpu, memory, io, and pids must be explicitly delegated down the systemd cgroup tree before a leaf cgroup can use them. When Docker creates the container’s scope and tries to write cpu.max or memory.max, the controller file does not exist in that sub-tree because the parent slice never enabled it via cgroup.subtree_control. The write fails, and runc aborts container init.
This is a host configuration gap, not a bug in your image. The container itself is fine; the kernel simply refuses to apply a limit through a controller that was never delegated.
Common Causes
- The host runs cgroup v2 but the
cpu/memorycontrollers were never delegated to thedockerorsystem.slicesub-tree. - Running rootless Docker under a user systemd slice where
Delegate=does not includecpu(older systemd delegated onlymemoryandpidsfor non-root users). - A minimal or embedded kernel compiled without
CONFIG_CGROUP_CPU/CONFIG_MEMCGsupport. systemd.unified_cgroup_hierarchy=1was enabled but the CPU controller was left masked.- Applying
--cpus,--cpu-quota, or--memoryflags on a host that cannot honour them.
Diagnostic Commands
Confirm you are actually on cgroup v2:
docker info --format '{{.CgroupVersion}} {{.CgroupDriver}}'
stat -fc %T /sys/fs/cgroup
cgroup2fs from stat confirms the unified hierarchy.
Check which controllers are available and which are delegated at the root:
cat /sys/fs/cgroup/cgroup.controllers
cat /sys/fs/cgroup/cgroup.subtree_control
For rootless Docker, inspect the user slice delegation:
cat /sys/fs/cgroup/user.slice/user-$(id -u).slice/cgroup.controllers
Reproduce the failure with an explicit limit:
docker run --rm --cpus=1 --memory=256m alpine true
Step-by-Step Resolution
-
Verify the controller is missing from the sub-tree, not from the kernel. If
cpu/memoryappear in/sys/fs/cgroup/cgroup.controllersbut not incgroup.subtree_control, the fix is delegation. If they are missing fromcgroup.controllersentirely, the kernel lacks support and must be rebuilt or replaced. -
For rootless Docker, enable CPU delegation for the user by creating a systemd drop-in:
sudo mkdir -p /etc/systemd/system/user@.service.d
sudo tee /etc/systemd/system/user@.service.d/delegate.conf <<'EOF'
[Service]
Delegate=cpu cpuset io memory pids
EOF
sudo systemctl daemon-reload
- Reboot, or log the user out and back in, so the new delegation applies to the user slice. Then confirm:
cat /sys/fs/cgroup/user.slice/user-$(id -u).slice/cgroup.controllers
- For rootful Docker, ensure the daemon uses the systemd cgroup driver so scopes inherit delegated controllers. Edit
/etc/docker/daemon.json:
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
- Restart the daemon and re-test:
sudo systemctl restart docker
docker run --rm --cpus=1 --memory=256m alpine true
- If the container now starts without the
controller not availableerror, the delegation is working. Confirm the limit is live:
docker run -d --name limited --memory=256m nginx
docker exec limited cat /sys/fs/cgroup/memory.max
Prevention
- Standardise on systemd as both the init and the Docker cgroup driver so controller delegation is consistent across hosts.
- Bake the
Delegate=cpu cpuset io memory pidsdrop-in into your rootless-Docker provisioning so every user slice can enforce limits. - Validate
docker run --cpus=1 --memory=128m alpine truein image bake or CI to catch a host that cannot apply limits before workloads land on it. - Keep kernels current; distro kernels since 5.x ship the unified-hierarchy controllers, but stripped-down or IoT kernels may not.
- Document which nodes are cgroup v1 versus v2 so scheduling and limit expectations match reality.
Related Errors
Your kernel does not support swap limit capabilities— a related cgroup gap, specific to swap accounting rather than cpu/memory.OCI runtime create failed: ... unable to apply cgroup configuration— a broader runc cgroup failure that includes this controller case.cannot enter cgroupv2 ... with domain controllers— a threaded/domain cgroup conflict, distinct from missing delegation.WARNING: No memory limit support— the non-fatal warning form that precedes silently ignored limits.
Frequently Asked Questions
How do I know if my host is cgroup v1 or v2? Run docker info --format '{{.CgroupVersion}}', or stat -fc %T /sys/fs/cgroup — cgroup2fs means the unified v2 hierarchy is active.
Why does the limit work as root but fail in rootless mode? Older systemd only delegates memory and pids to user slices by default. Adding Delegate=cpu ... via a user@.service.d drop-in grants the CPU controller to rootless containers. A generic prompt from the DevOps AI prompt library at /prompts/?stack=docker can help you template these drop-ins.
Do I have to reboot after adding the delegation drop-in? You need the user session to restart. A full reboot is simplest; otherwise log the user out completely (ending its user@.service) and back in so the new Delegate= takes effect.
Can I just ignore the limits and run without them? You can drop --cpus/--memory and the container will start, but it will be unbounded. On shared hosts that risks noisy-neighbour and OOM problems, so fixing delegation is the better path.
Is switching back to cgroup v1 a valid fix? It works as a stopgap by booting with systemd.unified_cgroup_hierarchy=0, but v1 is deprecated. Prefer delegating controllers on v2 rather than regressing the whole host. For more container-runtime fixes, see the Docker guides.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.