Images, services and default settings

GitLab CI/CD · Essentials · 10 min

Tests usually need a specific toolchain and a real database, and services provide both without extra infrastructure.

With the Docker executor every job runs inside the image named by image. Pin a tag (postgres:17, node:22-alpine) rather than latest so pipelines are reproducible. Images can come from Docker Hub, GitLab's own container registry ($CI_REGISTRY_IMAGE) or any registry the runner can reach.

Services are extra containers started alongside the job, typically databases, caches or brokers. The job reaches a service by a hostname derived from the image name (postgres:17 becomes postgres) or by an explicit alias. Service containers receive the job's variables, which is how you set the database password.

default: applies keywords to every job unless a job overrides them: image, services, before_script, after_script, tags, retry, timeout, interruptible and a few others.

Building Docker images inside a job requires access to a Docker daemon. The common patterns are a docker:dind service (Docker-in-Docker, needs a privileged runner) or a daemonless builder such as BuildKit or Kaniko. GitLab provides $CI_REGISTRY, $CI_REGISTRY_USER and $CI_REGISTRY_PASSWORD so jobs can push to the project's registry without storing credentials.

Example

default:
  image: python:3.13-slim
  retry: 1

integration:
  services:
    - name: postgres:17
      alias: db
  variables:
    POSTGRES_PASSWORD: test       # also passed to the service container
    DATABASE_URL: postgresql://postgres:test@db:5432/postgres
  script:
    - pip install -r requirements.txt
    - pytest tests/integration

build-image:
  image: docker:28
  services: [docker:28-dind]
  script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
    - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" .
    - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" 

Remember

  • image sets the job container; pin its tag
  • services start side containers reachable by name or alias
  • default: shares image, before_script, retry and more across jobs

Watch out for

  • Connecting to localhost for a service: with the Docker executor the service has its own hostname.

Check yourself

  1. A job has services: [redis:7]. Which hostname reaches Redis by default?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.gitlab.com/ci/services/