Running systemd inside Docker containers has always been tricky, especially with the transition to cgroups v2. The error message Failed to create /init.scope control group: Read-only file system
typically appears when:
- Host system uses cgroups v2 (default in newer Linux kernels)
- Docker container tries to use private cgroup namespace
- Incorrect volume mounts for cgroup filesystem
With cgroups v2, Docker's default behavior changes significantly. The key differences:
# cgroups v1 behavior (legacy)
/sys/fs/cgroup/systemd/docker/CONTAINER_ID/
# cgroups v2 behavior (modern)
/sys/fs/cgroup/system.slice/docker-CONTAINER_ID.scope/
Systemd expects write access to certain cgroup paths to create its scope units. When using --cgroupns=private
, the container's view of the cgroup hierarchy becomes isolated and may conflict with systemd's expectations.
Option 1: Shared Cgroup Namespace
The most reliable approach is sharing the host's cgroup namespace:
docker run --cgroupns=host \
--tmpfs /run \
--tmpfs /run/lock \
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
your_image
Option 2: Systemd-specific Workarounds
For cases where namespace isolation is required, modify your Dockerfile:
RUN echo 'kernel.unprivileged_userns_clone=1' >> /etc/sysctl.conf \
&& mkdir -p /sys/fs/cgroup/systemd \
&& mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd
Option 3: Docker Compose Configuration
For production deployments using compose:
version: '3.8'
services:
systemd-service:
image: your_image
cgroup: host
tmpfs:
- /run
- /run/lock
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
Docker's cgroups v2 support means:
- Containers are placed in the unified hierarchy
- Resource limits are enforced via cgroup2 controllers
- Systemd integration requires special handling
The key insight is that systemd must be able to:
- Create its scope units
- Write to the cgroup.procs file
- Manage delegated controllers
For those who can't modify run configurations:
# Use podman instead of docker
podman run --systemd=true -it your_image
# Or disable cgroups v2 on host
GRUB_CMDLINE_LINUX="systemd.unified_cgroup_hierarchy=0"
When running systemd inside Docker containers on hosts using cgroupv2 (default in newer Linux kernels), you'll encounter a specific class of errors. The fundamental issue stems from how systemd expects to manage cgroups versus how Docker implements namespacing in cgroupv2 environments.
Here's the minimal test case that demonstrates the problem:
FROM debian:buster-slim
RUN apt-get update && apt-get install -y systemd
VOLUME ["/sys/fs/cgroup"]
CMD ["/lib/systemd/systemd"]
When running this container with default cgroupv2 settings:
docker run --tmpfs /run --tmpfs /run/lock --tmpfs /tmp \
-v /sys/fs/cgroup:/sys/fs/cgroup:rw -it test-image
You'll see the error:
Failed to create /init.scope control group: Read-only file system
Failed to allocate manager object: Read-only file system
The key differences between cgroupv1 and v2 that affect this scenario:
1. In cgroupv2, all controllers are mounted under a single hierarchy
2. Docker's default --cgroupns=private creates a new cgroup namespace
3. Systemd expects to write to specific cgroup paths that become inaccessible
Option 1: Use host cgroup namespace
docker run --cgroupns=host --tmpfs /run \
-v /sys/fs/cgroup:/sys/fs/cgroup:rw -it image_name
Option 2: Enable systemd legacy hierarchy
Add this to your Dockerfile:
RUN mkdir -p /etc/systemd/system.conf.d && \
echo -e '[Manager]\nDefaultCPUAccounting=no\nDefaultMemoryAccounting=no' > \
/etc/systemd/system.conf.d/docker.conf
Option 3: Full cgroupv2 compatibility mode
For newer systemd versions (242+), use:
docker run --privileged --tmpfs /run \
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
-e SYSTEMD_UNIFIED_CGROUP_HIERARCHY=1 -it image_name
For production deployments using systemd in containers:
1. Always use a read-write mount for /sys/fs/cgroup
2. Consider using podman instead of docker for better systemd integration
3. If possible, upgrade to Debian bullseye or newer for better cgroupv2 support
# Example production-ready Dockerfile snippet
FROM debian:bullseye-slim
RUN apt-get update && \
apt-get install -y systemd && \
rm -rf /var/lib/apt/lists/*
RUN systemctl mask dev-hugepages.mount sys-fs-fuse-connections.mount
VOLUME ["/sys/fs/cgroup"]
CMD ["/lib/systemd/systemd", "--log-level=info", "--log-target=console"]