Skip to content
Latchkey

How to Run Docker-in-Docker in GitLab CI

Docker-in-Docker lets a GitLab job build and push images by running a docker:dind service alongside a docker client image.

Add the docker:dind service, point DOCKER_HOST at it over TLS, and authenticate to the GitLab Container Registry with the built-in $CI_REGISTRY credentials.

Build and push an image

The dind service provides the Docker daemon; the job logs in with the predefined registry variables.

.gitlab-ci.yml
build:image:
  image: docker:27
  services:
    - docker:27-dind
  variables:
    DOCKER_HOST: tcp://docker:2376
    DOCKER_TLS_CERTDIR: "/certs"
  script:
    - echo "${CI_REGISTRY_PASSWORD}" | docker login -u "${CI_REGISTRY_USER}" --password-stdin "${CI_REGISTRY}"
    - docker build -t "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}" .
    - docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}"

Gotchas

  • dind requires a privileged runner; shared SaaS runners support it, but self-managed ones may not.
  • Keep DOCKER_TLS_CERTDIR set and use port 2376 (TLS); disabling TLS is insecure and error-prone.
  • Use BuildKit or layer caching to avoid rebuilding every layer on each run.

Key takeaways

  • Run docker:dind as a service and point DOCKER_HOST at it over TLS.
  • Authenticate with the predefined $CI_REGISTRY* variables.
  • dind needs a privileged runner; not all self-managed runners allow it.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →