CI/CD

GitLab CI Docker-in-Docker Errors: The Complete Debug Checklist

DinD failing in GitLab CI? The 3 most common Docker-in-Docker failure signatures with their actual log output, exact fixes, and when to use Kaniko or buildah instead.

D

Daxtack Team

Engineering

8 min read

Docker-in-Docker (DinD) in GitLab CI is one of those things that works perfectly once it's set up correctly — and is absolutely bewildering when it doesn't. The error messages are cryptic, the configuration has multiple interacting pieces, and most tutorials gloss over the parts that actually break.

This is a reference checklist. The 3 most common DinD failure modes, with the actual log output you'll see, and the exact fix for each.

What DinD Is and Why GitLab CI Needs It

When your CI job needs to build Docker images, it needs access to a Docker daemon. But your CI job is already running inside a container on the GitLab runner. So you need Docker inside Docker — hence "DinD."

GitLab supports this through the docker:dind service, which runs a Docker daemon as a sidecar container alongside your job. Your job container connects to this sidecar's Docker socket to run Docker commands.

This architecture has three points of failure, and each one produces a different error.

Failure 1: Missing or Misconfigured DinD Service

Log output you'll see:

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
ERROR: error during connect: Post "http://docker:2376/v1.24/build": dial tcp: lookup docker on 10.0.0.10:53: no such host

Cause: The docker:dind service isn't configured, or the job is trying to connect to the wrong Docker host.

Fix:

build_image:
  image: docker:24.0       # Use the Docker CLI image
  services:
    - docker:24.0-dind      # Run DinD as a service
  variables:
    DOCKER_HOST: tcp://docker:2376     # Connect to the DinD service
    DOCKER_TLS_CERTDIR: "/certs"       # Enable TLS
    DOCKER_CERT_PATH: "/certs/client"  # Client cert location
    DOCKER_TLS_VERIFY: "1"             # Verify TLS
  script:
    - docker info
    - docker build -t myapp .

Key points:

  • The image and services versions should match (both docker:24.0)
  • DOCKER_HOST must be tcp://docker:2376 (TLS) or tcp://docker:2375 (no TLS) — not unix:///var/run/docker.sock
  • The hostname docker is a special alias that GitLab sets up for the service container

Failure 2: TLS Certificate Misconfiguration

Log output you'll see:

error during connect: Post "https://docker:2376/v1.24/images/create": tls: failed to verify certificate: x509: certificate signed by unknown authority
ERROR: error during connect: Get "https://docker:2376/v1.24/containers/json": write tcp 172.17.0.3:54120->172.17.0.2:2376: write: connection reset by peer

Cause: The DOCKER_TLS_CERTDIR variable is misconfigured. When DinD starts with TLS enabled, it generates certificates in /certs. These need to be shared with the client container via a volume. If the variable is empty, set wrong, or the volume mount doesn't work, TLS handshakes fail.

Fix — Option A: Configure TLS correctly (recommended):

variables:
  DOCKER_TLS_CERTDIR: "/certs"

services:
  - docker:24.0-dind

# The /certs directory is shared between the service and job containers
# via GitLab's automatic service volume sharing

Fix — Option B: Disable TLS entirely (simpler but less secure):

variables:
  DOCKER_TLS_CERTDIR: ""           # Empty = no TLS
  DOCKER_HOST: tcp://docker:2375   # Port 2375 = no TLS

services:
  - docker:24.0-dind

Option B is fine for CI — you're communicating between containers on the same host, so TLS isn't adding meaningful security. But option A is better practice.

Failure 3: Runner Privileged Mode Not Enabled

Log output you'll see:

ERROR: Cannot start service docker: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: Running in a user namespace with a privileged service container is not supported

Or sometimes just:

ERROR: Job failed: prepare environment: Error response from daemon: authorization denied by plugin

Cause: DinD requires the Docker container to run in privileged mode. If the GitLab runner isn't configured to allow privileged containers, the DinD service can't start.

Fix: This requires runner configuration changes (you can't fix this in .gitlab-ci.yml):

# /etc/gitlab-runner/config.toml
[[runners]]
  [runners.docker]
    privileged = true          # Required for DinD
    volumes = ["/certs/client"]  # Share TLS certs

If you're using GitLab.com's shared runners, privileged mode is already enabled. If you're using self-hosted runners, you (or your platform team) need to enable it.

Security note: Privileged containers have full access to the host kernel. This is inherently risky. If this concerns you, consider the alternatives below.

When to Avoid DinD Entirely

DinD works, but it has real downsides: security concerns with privileged mode, performance overhead from the nested Docker daemon, and the configuration complexity you've just seen. Alternatives:

Kaniko (Google)

build_image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.22.0-debug
    entrypoint: [""]
  script:
    - /kaniko/executor
      --context $CI_PROJECT_DIR
      --dockerfile Dockerfile
      --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

Kaniko builds images without a Docker daemon. No privileged mode needed. It's slower for complex builds but safer and simpler to configure.

Buildah

build_image:
  stage: build
  image: quay.io/buildah/stable
  variables:
    STORAGE_DRIVER: vfs
  script:
    - buildah bud -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
    - buildah push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

Buildah is OCI-compliant and daemonless. Good for teams already in the Red Hat ecosystem.

Quick Decision Table

ApproachPrivileged?SpeedComplexityBest For
DinDYesFastMediumFull Docker compatibility needed
KanikoNoModerateLowSimple image builds
BuildahNoModerateLowRed Hat/OCI ecosystem
Docker socket bindingYes (effectively)FastestLowTrusted environments only

These errors look different every time but the root cause is one of three things. Tools like Daxtack pattern-match against known failure signatures like this automatically, which is faster than eyeballing it — but the checklist above works fine manually too.

GitLab CIDockerDocker-in-DockerDinDCI/CDContainersKanikoDebugging

Debug CI/CD failures in 30 seconds

Daxtack uses AI to automatically analyze your build logs, find the root cause, and suggest fixes — right in your pull request.

Related Articles