GitLab CI Docker-in-Docker (dind) Error Fix
In this tutorial, you'll learn about GitLab CI Docker. We cover key concepts, practical examples, and best practices.
Your GitLab CI job fails with Cannot connect to the Docker daemon or tls: bad certificate — the Docker-in-Docker (dind) service isn't configured correctly or the runner doesn't support privileged mode.
The Problem
# WRONG — dind service without TLS configuration
build:
image: docker:latest
services:
- docker:dind
script:
- docker build -t myapp .
Cannot connect to the Docker daemon at tcp://docker:2376. Is the docker daemon running?
The Docker client expects TLS connection (port 2376) but the dind service may not have TLS configured, or the environment variables for TLS are missing.
Step-by-Step Fix
1. Use the official dind template with TLS
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_DRIVER: overlay2
build:
image: docker:latest
services:
- name: docker:dind
entrypoint: ["dockerd-entrypoint.sh"]
script:
- docker build -t myapp .
- docker tag myapp $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
2. Use Docker socket binding for simpler setup
variables:
DOCKER_HOST: unix:///var/run/docker.sock
DOCKER_DRIVER: overlay2
build:
image: docker:latest
script:
- docker build -t myapp .
services: []
Socket binding mounts the host's Docker socket into the container. This is simpler and faster but less secure (the container has full Docker access on the host).
3. Configure the runner for privileged mode
# config.toml on the runner
[[runners]]
privileged = true
Check Settings > CI/CD > Runners that the runner has privileged mode enabled. Without it, Docker commands fail.
4. Use Kaniko as an alternative to dind
build:
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- /kaniko/executor
--context=$CI_PROJECT_DIR
--destination=$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
Kaniko builds Docker images without Docker privileges, making it more secure.
Expected output:
Step 1/5 : FROM node:20-alpine
Step 2/5 : WORKDIR /app
Step 3/5 : COPY package*.json ./
Step 4/5 : RUN npm ci
Step 5/5 : COPY . .
Successfully built abc123def456
Successfully tagged myapp:latest
Prevention Tips
- Use the Docker socket binding for simplicity in non-sensitive projects
- Use Kaniko for production GitLab pipelines with security requirements
- Always set
DOCKER_HOSTandDOCKER_TLS_CERTDIRwith dind - Enable
privilegedmode on the GitLab runner - Use
docker infoas a first step to verify Docker connectivity
Common Mistakes with ci dind
- Using
foldlinstead offoldl'causing stack overflow on large lists - Forgetting
deriving (Show, Eq)on custom data types needed for debugging - Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
These mistakes appear frequently in real-world GITLAB code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro