Skip to content

GitLab CI Docker-in-Docker (dind) Error Fix

DodaTech Updated 2026-06-24 3 min read

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_HOST and DOCKER_TLS_CERTDIR with dind
  • Enable privileged mode on the GitLab runner
  • Use docker info as a first step to verify Docker connectivity

Common Mistakes with ci dind

  1. Using foldl instead of foldl' causing stack overflow on large lists
  2. Forgetting deriving (Show, Eq) on custom data types needed for debugging
  3. 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

### What's the difference between docker:dind and using the Docker socket?

docker:dind runs a full Docker daemon inside a container — it's isolated but requires privileged mode and more resources. The Docker socket (/var/run/docker.sock) binds to the host's Docker daemon — it's faster but the container has host-level Docker access. Use dind for isolation, socket for speed.

Why do I get "tls: bad certificate" with dind?

The TLS certificates in /certs don't match the hostname or are missing. Set DOCKER_TLS_CERTDIR: "/certs" to enable TLS. Alternatively, disable TLS by setting DOCKER_TLS_CERTDIR: "" and using DOCKER_HOST: tcp://docker:2375 (non-TLS port). TLS is recommended for production.

Should I use Kaniko or dind for building images?

Use Kaniko if your GitLab runner can't run privileged containers or if security policies prevent dind. Use dind if you need full Docker features (buildx, multi-platform, layer caching) and the runner supports privileged mode. Kaniko is more secure but slower for complex builds.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro