title: Docker Build Strategies in GitLab CI/CD: Technical Comparison and Selection Criteria for DinD and DooD meta_description: Explains the architecture, security, and implementation constraints of DinD (Docker-in-Docker) and DooD (Docker-out-of-Docker) for building Docker containers in GitLab CI/CD pipelines from a technical perspective.
In GitLab CI/CD pipelines, the requirement to execute docker build or docker push within a containerized runner is common. Two main architectural patterns exist for achieving this “container-in-container” approach: Docker-in-Docker (DinD) and Docker-out-of-Docker (DooD). This article analyzes their respective technical structures, security implications, and fatal conflict issues that occur during implementation.
Reconfirming Docker’s Client-Server Model
To understand the differences between DinD and DooD, it is necessary to recognize that Docker is a client-server architecture. Docker commands are separated into the following two components:
- Docker CLI (Client): The interface that receives user commands and sends them to the server.
- dockerd (Daemon/Server): The background process that actually executes image builds, volume management, and container orchestration.
When running Docker inside a container, “where the Docker Daemon exists” is the core of the architecture.
Structure and Characteristics of Docker-in-Docker (DinD)
DinD is a method of running a completely independent Docker Daemon inside a container.
Mechanism
A dedicated “service” container runs the DinD image and initializes its own isolated Docker Daemon. The build container’s CLI typically connects to this internal daemon via a network socket such as tcp://docker:2375.
Necessity of Privileged Mode
To operate DinD, privileged = true must be enabled in the GitLab Runner configuration. This is because the internal daemon requires high-level access to kernel features, such as creating cgroups and managing network namespaces.
Pros and Cons
- Pros: Suitable for multi-tenant environments as it is completely isolated from the host environment and other build jobs.
- Cons: High overhead and a tendency for performance degradation because a new daemon is started for each job. It also carries security risks associated with the use of privileged mode.
Structure and Characteristics of Docker-out-of-Docker (DooD)
DooD is a method where the CLI inside the container communicates directly with the host machine’s Docker Daemon.
Mechanism
Achieved by mounting the host’s Docker socket file (/var/run/docker.sock) into the container.
Configuration Example
[runners.docker]
volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
Also, specify environment variables in .gitlab-ci.yml.
variables:
DOCKER_HOST: unix:///var/run/docker.sock
Pros and Cons
- Pros: Faster and simpler to implement than DinD because no additional daemon startup is required.
- Cons: Zero isolation. Since the container shares the host’s daemon, it can manipulate all images and containers on the host. Mounting docker.sock is essentially synonymous with granting host root privileges to the container, making the risk of container escape extremely high.
Comparison of Security Hierarchies
The security strength of each method is as follows (higher security toward the bottom).
- DooD: Lowest. Allows host-level root access via socket mounting.
- DinD: Moderate. Isolated from the host, but requires privileged mode, which could become an attack vector to the host if the container is compromised.
- BuildKit (rootless): Highest. A modern standard approach that operates daemonless without requiring privileged access or socket mounting.
Technical Constraint: Mutual Exclusivity of DinD and DooD
As an important engineering note, DinD and DooD cannot be used together within a single GitLab Runner configuration. Attempting to mix them will cause jobs to fail due to the following logical breakdown:
- If a volume mount for /var/run/docker.sock is described in the GitLab Runner’s config.toml, this mount is applied to all containers (including service containers) generated by that runner.
- When the DinD service container starts, it attempts to initialize a Docker Daemon inside itself.
- The DinD daemon attempts to create its own socket at /var/run/docker.sock, but a socket mounted from the host already exists at that location.
- The system returns a “device or resource busy” error, and the DinD daemon fails to start.
Therefore, when using DinD, the /var/run/docker.sock mount must be explicitly excluded from config.toml. The design principle is to have a runner dedicated to a single method.
Operational Notes
Criteria for selecting an implementation strategy are as follows:
- Multi-tenant / High Security Requirements: Select DinD to ensure isolation between jobs.
- Single User / Speed Priority: Select DooD to minimize overhead.
- Modern CI/CD Environments: Consider adopting 🛠️ BuildKit (rootless). BuildKit supports multi-architecture builds and native secret management without requiring privileged mode, achieving both performance and security.