Docker-in-Docker

Last update: 2022-09-14 14:25:00 +0800 +0800
Available translations: 日本語 / 中文(简体) / 中文(繁體)

Docker-in-Docker (DinD) runs a Docker daemon inside a container. It is sometimes useful for disposable CI jobs that must build or test images, but it adds privileged-kernel access, nested storage, and caching complexity. It is not the default choice for every job that invokes docker.

Choose the Right Build Model #

There are three common models:

  1. DinD: a dedicated daemon runs in the job network namespace. It provides separation from the host daemon but commonly needs privileged mode.
  2. Docker-outside-of-Docker: the job mounts the host Docker socket. It is simple and fast, but any process that can use the socket can control the host Docker daemon.
  3. Daemonless builders: BuildKit, Kaniko, Buildah, or a managed builder create OCI images without exposing a general-purpose host daemon.

Use the least privileged option that supports the build. Rootless BuildKit or a remote BuildKit builder can preserve cache efficiency without granting a CI job access to the runner’s Docker socket.

Run a DinD Service with TLS #

The official docker:dind image starts dockerd; it is intended to be paired with a separate Docker CLI container or CI job. Keep the daemon on an isolated network and use TLS for its TCP listener:

sh
docker network create ci-build
docker volume create dind-certs

docker run -d --name dind --privileged --network ci-build \
  -e DOCKER_TLS_CERTDIR=/certs \
  -v dind-certs:/certs/client \
  docker:27-dind

docker run --rm --network ci-build \
  -e DOCKER_HOST=tcp://dind:2376 \
  -e DOCKER_TLS_VERIFY=1 \
  -e DOCKER_CERT_PATH=/certs/client \
  -v dind-certs:/certs/client:ro \
  docker:27-cli version

The --privileged flag gives the service broad host-kernel capabilities. Run it only on an isolated, disposable runner. Do not expose an unauthenticated dockerd TCP port: anyone who can reach it can create privileged containers and usually take control of its host.

Handle Cache and Cleanup #

DinD’s image and layer cache lives in the service container’s /var/lib/docker. A persistent cache volume speeds repeated builds but must be scoped to a trusted project or tenant; image layers can contain source code and build secrets. Prefer registry-backed BuildKit cache for shared CI infrastructure.

Use unique tags based on an immutable commit identifier, push only after tests pass, and remove temporary containers and images according to the runner’s retention policy. Never pass secrets through Dockerfile ARG or ENV; use BuildKit secret mounts or the CI system’s secret mechanism instead.

Comments

Read or comment on GitHub (sign-in required to comment)