GitLab Runner: Building a Docker image with Docker-in-Docker
This documentation is part of the Building Docker images guide. You can view the complete guide here: Build and push Docker images efficiently from your GitLab CI/CD pipelines using Stackhero runners and Docker-in-Docker.
👋 Welcome to the Stackhero documentation!
Stackhero offers a simple GitLab Runner cloud solution that makes running your GitLab CI/CD jobs efficient and straightforward. Here’s what you can expect:
- Unlimited CI/CD minutes: run your pipelines as often as you need, with no per-minute billing or unexpected fees.
- Multiple concurrent jobs: speed up your development by running several jobs in parallel.
- The Docker executor with Docker-in-Docker support: easily build and push container images as part of your CI/CD workflows.
- Works seamlessly with both GitLab.com and self-managed GitLab instances.
- A private, dedicated infrastructure with fast NVMe/SSD storage ensures stable and predictable build performance.
- Available in 🇪🇺 Europe and 🇺🇸 USA regions to meet your team's needs.
Save time: you can connect your first GitLab Runner and start running pipelines in just a few minutes!
You can add the following sample .gitlab-ci.yml to your repository. This configuration builds the Dockerfile located at the root of your project:
build-image:
stage: build
image: docker:29
services:
- name: docker:29-dind
alias: docker
variables:
DOCKER_HOST: "tcp://docker:2375"
DOCKER_TLS_CERTDIR: ""
before_script:
- docker info
script:
# Replace "my-image" with your desired name:
- docker build -t my-image .
# Optionally, run a quick test on the built image:
# - docker run --rm my-image /path/to/tests
We are using Docker image version 29 in this example. You may want to use a newer version as they become available. You can find the latest tags on the official Docker image page.
In this setup, the docker:29-dind service starts a Docker daemon next to your job, and DOCKER_HOST: "tcp://docker:2375" tells the docker CLI to use it. Always set DOCKER_HOST when you declare a docker:dind service: without it, the CLI silently connects to a different daemon, so your build can pass even when the service is misconfigured, which hides real problems (and breaks tools like Testcontainers that rely on the service). DOCKER_TLS_CERTDIR: "" connects over the plain, non-TLS port 2375 on the internal job network.
As a simpler alternative, you can omit the services block and both variables: Stackhero's runner also exposes a ready-to-use Docker daemon through a mounted socket, so a bare docker build works without any extra configuration.