GitLab Runner: Building Docker images
Build and push Docker images from your GitLab CI/CD pipelines using your Stackhero runner and Docker-in-Docker
👋 Welcome to the Stackhero documentation!
Stackhero offers a ready-to-use GitLab Runner cloud solution that runs your GitLab CI/CD jobs, with benefits including:
- Unlimited CI/CD minutes: no per-minute billing, run your pipelines around the clock.
- Dozens of concurrent jobs so a whole pipeline stage runs at once.
- The Docker executor with Docker-in-Docker support to build and push your images.
- Works with GitLab.com and any self-managed GitLab.
- A private and dedicated VM with NVMe/SSD disks for fast, consistent builds.
- Available in 🇪🇺 Europe and 🇺🇸 USA.
Save time: it only takes a few minutes to connect your first GitLab Runner and start running pipelines!
Introduction
Your Stackhero GitLab Runner uses the Docker executor: every job runs in a clean container based on the image you choose. To build your own images inside a pipeline, you use Docker-in-Docker (DinD), where a Docker daemon runs alongside your job so you can run docker build and docker push.
Because your runner has unlimited CI/CD minutes, you can build images as often as you need, and because the build cache lives on the runner dedicated disk, repeat builds reuse previous layers and finish much faster.
Build a Docker image with Docker-in-Docker
Add the following .gitlab-ci.yml to your repository. It builds the Dockerfile at the root of your project:
build-image:
stage: build
image: docker:27
services:
- docker:27-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker info
script:
# Replace "my-image" with the name you want:
- docker build -t my-image .
# Optionally run a quick smoke test on the built image:
# - docker run --rm my-image /path/to/tests
The docker:27-dind service starts the Docker daemon, and DOCKER_TLS_CERTDIR: "/certs" enables the secure TLS connection between your job and that daemon.
Push to the GitLab Container Registry
GitLab exposes a few predefined variables (CI_REGISTRY, CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, CI_REGISTRY_IMAGE) so a job can log in and push to the project Container Registry with no extra secret to manage:
build-and-push:
stage: build
image: docker:27
services:
- docker:27-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
- docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" .
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
# Also tag and push "latest" on the default branch:
- |
if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
docker tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" "$CI_REGISTRY_IMAGE:latest"
docker push "$CI_REGISTRY_IMAGE:latest"
fi
To push to another registry (Docker Hub, a registry on your own infrastructure, etc.), store its credentials as CI/CD variables and use them in docker login instead.
Speed up repeat builds
Your runner keeps its disk between pipelines, so you can reuse the layers of a previous build to make the next one faster:
build-cached:
stage: build
image: docker:27
services:
- docker:27-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
# Pull the previous image to seed the layer cache (ignore the error on the first build):
- docker pull "$CI_REGISTRY_IMAGE:latest" || true
- docker build --cache-from "$CI_REGISTRY_IMAGE:latest" -t "$CI_REGISTRY_IMAGE:latest" .
- docker push "$CI_REGISTRY_IMAGE:latest"
Run jobs in parallel
Your plan defines how many jobs run at the same time. Jobs in the same stage start together, up to your concurrency limit, so a stage with several independent jobs finishes in the time of its slowest job instead of running them one after another:
stages:
- test
unit:
stage: test
image: node:22
script: npm run test:unit
integration:
stage: test
image: node:22
script: npm run test:integration
e2e:
stage: test
image: node:22
script: npm run test:e2e
With a concurrency of 3 or more, unit, integration and e2e all run at once.
For more details on building images in CI, see the official GitLab documentation.