Docker-in-Docker Without Privileged Containers
Usually, Docker containers are deployed on a regular, non-containerized host system. Still, there are valid use cases that require a configuration known as Docker-in-Docker (DinD), where a Docker container is running on the host system and within that container runs yet another Docker container. The usual approach is to run the host container with the --privileged
option and use a special docker:dind
image.
A great disadvantage of privileged containers is that sandboxing is virtually eliminated — the container can do almost everything the host can, even obtain a root shell in the host system. Unfortunately, with a standard Docker setup, DinD cannot be achived in an unprivileged container.
Sysbox Container Runtime
To run DinD in an unprivileged container, you need to have control over the host system. Then, you can install an alternative container runtime that supports running the Docker daemon inside an unprivileged container: Sysbox.
Behind the scenes, going from the highest level to the lowest, Docker uses containerd to manage containers, and containerd uses the runc container runtime to spawn the containers. Sysbox is one of the alternatives to runc that implement the shim API, and can therefore be used with containerd, consequently with Docker. When you install Sysbox, it won’t interfere with runc, which will remain the default container runtime.
To install Sysbox, you can either use a prebuilt package for supported Linux distributions, or compile it from source. Refer to the official installation instructions.
Using Sysbox DinD Containers
Before starting any container that uses the Sysbox runtime, the sysbox
service must be running:
[host] # systemctl start sysbox
Then, when starting the container, set the --runtime
option to sysbox-runc
. Do not use the docker:dind
image; Sysbox doesn’t require special workarounds for running DinD. For example:
[host] # docker run --runtime=sysbox-runc -it --rm archlinux:latest bash
In the container, install Docker as you would on a non-containerized host. Prior to running any docker
commands, the Docker daemon must be running. This is usually done with a systemd service, however, container images typically don’t include systemd. You can spawn dockerd
in the background manually. For example:
[container] # pacman -Sy docker
[container] # dockerd >/var/log/dockerd.log 2>&1 &
[container] # docker run -it alpine:latest sh
If you’re building your own container image, you can create a wrapper around the docker
executable that spawns dockerd
when needed. For example:
FROM archlinux:latest
RUN pacman -Sy --noconfirm docker docker-compose; \
echo '#!/bin/sh' > /usr/local/sbin/docker; \
echo 'if ! (pgrep dockerd >/dev/null 2>&1); then' >> /usr/local/sbin/docker; \
echo ' dockerd >/var/log/dockerd.log 2>&1 &' >> /usr/local/sbin/docker; \
echo 'fi' >> /usr/local/sbin/docker; \
echo 'exec /usr/sbin/docker "$@"' >> /usr/local/sbin/docker; \
chmod 755 /usr/local/sbin/docker
ENTRYPOINT ["bash"]