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 straightforward GitLab Runner cloud solution that makes running your GitLab CI/CD jobs efficient and hassle-free. 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 charges.
  • 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 process.
  • 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 requirements.

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 wish 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 if 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 via a mounted socket, so a basic docker build works without any additional configuration.