Skip to content
DevOps AI ToolKit
Newsletter
All guides
Docker with AI By James Joyner IV · · 9 min read

Docker Error Guide: 'controller not available' cgroup v2 Resource Limits

Quick answer

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.

Part of the Docker Container & Runtime Errors hub
  • #docker
  • #troubleshooting
  • #errors
  • #cgroups
Free toolkit

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/memory controllers were never delegated to the docker or system.slice sub-tree.
  • Running rootless Docker under a user systemd slice where Delegate= does not include cpu (older systemd delegated only memory and pids for non-root users).
  • A minimal or embedded kernel compiled without CONFIG_CGROUP_CPU/CONFIG_MEMCG support.
  • systemd.unified_cgroup_hierarchy=1 was enabled but the CPU controller was left masked.
  • Applying --cpus, --cpu-quota, or --memory flags 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

  1. Verify the controller is missing from the sub-tree, not from the kernel. If cpu/memory appear in /sys/fs/cgroup/cgroup.controllers but not in cgroup.subtree_control, the fix is delegation. If they are missing from cgroup.controllers entirely, the kernel lacks support and must be rebuilt or replaced.

  2. 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
  1. 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
  1. 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"]
}
  1. Restart the daemon and re-test:
sudo systemctl restart docker
docker run --rm --cpus=1 --memory=256m alpine true
  1. If the container now starts without the controller not available error, 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 pids drop-in into your rootless-Docker provisioning so every user slice can enforce limits.
  • Validate docker run --cpus=1 --memory=128m alpine true in 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.
  • 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/cgroupcgroup2fs 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.

Free download · 368-page PDF

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.