Troubleshooting Systemd in Docker Containers with Cgroups v2: Solutions for “Read-only File System” Errors


1 views

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:

  1. Containers are placed in the unified hierarchy
  2. Resource limits are enforced via cgroup2 controllers
  3. 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"]