Skip to main content
GitLab CI/CD is one of the most complete CI platforms available today — the pipeline definition lives in the same repository as the code, the built-in container registry removes the need for a separate Docker Hub account, and the services keyword makes ephemeral sidecar containers (like a Docker daemon or a test database) trivially easy. These notes cover the patterns that come up on almost every real project.

.gitlab-ci.yml Anatomy

Every pipeline is defined in a .gitlab-ci.yml at the root of the repository. The top-level keys you’ll use on every project:
Stage order matters — jobs in stage n+1 only start after all jobs in stage n have passed. Jobs within the same stage run in parallel by default.

Key Built-in Variables

GitLab injects these into every job automatically — no configuration required:

Docker-in-Docker (DinD)

Running docker build inside a GitLab CI job requires a Docker daemon. The recommended approach is to use the official docker:dind service — GitLab spins it up as a sidecar container that the job container connects to over TLS.
DinD requires the runner to be configured with the docker executor (or Kubernetes executor with the right settings). It does not work on shell executors by default.

How It Works

The docker:dind service exposes the Docker socket over TCP (with TLS on port 2376). The job sets DOCKER_HOST to point at it.

Minimal DinD Job

Use docker:cli (not docker:latest) as the job image. It ships only the client binary, keeping the image small. The daemon lives exclusively in the docker:dind service container.

Official Docs

GitLab’s services keyword is documented at https://docs.gitlab.com/ci/services/. The services feature isn’t Docker-specific — you can use it to spin up PostgreSQL, Redis, or any other daemon your tests need.

Full Docker Build & Push Pipeline

This is a production-ready template for building a Docker image and pushing it to the built-in GitLab Container Registry on every push to main, plus every MR:

Caching & Artifacts

These two features are often confused. The distinction is important:
Cache speeds up jobs by persisting files between pipeline runs on the same runner. Use it for dependency directories (node_modules, .venv, Maven’s ~/.m2).
Cache is not guaranteed to be available — it’s a best-effort optimisation. Never rely on cache for correctness, only for speed.

Useful Patterns & Tips

Prefer rules over the legacy only/except keywords — it’s more expressive and evaluates top-to-bottom:

Docker Essentials

Commands, Dockerfiles, Compose, and networking — the building blocks used inside every pipeline.

Kubernetes

Deploy the images your GitLab pipeline builds into a Kubernetes cluster.
Last modified on June 9, 2026