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_CERTDIRset 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:dindas a service and pointDOCKER_HOSTat it over TLS. - Authenticate with the predefined
$CI_REGISTRY*variables. - dind needs a privileged runner; not all self-managed runners allow it.
Related guides
How to Use CI/CD Variables and Secrets in GitLab CIUse GitLab CI/CD variables for config and secrets, including predefined variables, masked and protected UI va…
How to Set Up Stages in GitLab CISet up pipeline stages in GitLab CI with the stages: keyword so jobs run in ordered phases like build, test,…