What is Docker-in-Docker (DinD), and what are its risks and alternatives?
Quick Answer
Docker-in-Docker means running a full, separate Docker daemon inside a container — typically needed when a CI pipeline itself runs inside a container but needs to build and run other Docker images. It carries real risks: running the outer container in --privileged mode, storage-driver complications, and generally weaker isolation than expected. That's why safer alternatives — mounting the host's Docker socket (with its own distinct risks, see the security topic), or a purpose-built rootless image-building tool like Kaniko or Buildah — are often preferred instead.
Detailed Answer
Why this scenario comes up at all
Many CI/CD systems run each job inside its own container, for isolation and reproducibility. But if that job's own work is to build a Docker image, a very common CI task, you end up needing to run Docker inside the container the CI job itself runs in. This is the scenario Docker-in-Docker addresses.
The DinD approach — a real, nested Docker daemon
docker run --privileged -d --name dind docker:24-dind
docker run --link dind:docker --env DOCKER_HOST=tcp://docker:2375 docker:24 docker build .
This runs an entirely separate Docker daemon inside a container, and a second container talks to that nested daemon to actually build images — genuinely "Docker inside Docker," not just talking to the host's existing daemon.
The real risks of this approach
- Requires
--privilegedmode. Running a nested Docker daemon generally requires disabling most container isolation for the outer container.--privilegedgrants nearly all capabilities and disables several security restrictions — a significant security relaxation, not a minor detail, and it directly undermines much of the isolation containers are meant to provide. - Storage-driver complications. Running a container filesystem (an overlay or union filesystem) inside another container's own overlay filesystem has historically caused real compatibility and performance issues, since it layers the same kind of filesystem trickery on top of itself.
- Weaker isolation than the "in Docker" framing suggests. Despite feeling "extra isolated" since it's Docker inside Docker, the
--privilegedrequirement actually means the outer container has less isolation from the host than an ordinary, non-privileged container would.
Alternative 1: mounting the host's Docker socket
docker run -v /var/run/docker.sock:/var/run/docker.sock docker:24 docker build .
This avoids running a nested daemon at all — the CI container talks directly to the host's own Docker daemon instead. It avoids DinD's storage-driver and --privileged concerns, but introduces its own well-documented, serious risk: socket access is functionally equivalent to host root. Any CI job with this mount effectively has host-level access, a serious concern for CI systems running untrusted or third-party pipeline code.
Alternative 2: purpose-built rootless image-building tools
# Kaniko, running inside a Kubernetes Pod with no special privileges,
# building an image without ever needing a Docker daemon (nested or host) at all
Tools like Kaniko (built by Google, common in Kubernetes-based CI) and Buildah can build OCI-compliant images without a Docker daemon at all. They implement the image-building logic directly in user space, with no need for privileged access or a socket to any daemon. This genuinely avoids both risks above, rather than just mitigating them, and is increasingly the preferred approach for CI systems, especially Kubernetes-based ones, that need to build images as an ordinary, unprivileged step in an otherwise-sandboxed pipeline.
Weighing the tradeoffs
| Approach | Privilege required | Risk profile |
|---|---|---|
| Docker-in-Docker (nested daemon) | --privileged | Significant isolation weakening; storage-driver quirks |
| Mounted host socket | None on the container itself, but socket access = host root | Serious, well-documented escalation risk |
| Kaniko / Buildah (daemonless) | None | Avoids both risks above entirely |
Where DinD or socket-mounting genuinely can't be avoided — some legacy pipeline setups, specific tooling requirements — treat that CI runner as a fully trusted, high-privilege environment, and restrict what pipelines are allowed to run in it accordingly. Don't treat it as just another routine, low-stakes CI job.