Runners, executors, and job execution environment
The Runners, executors, and job execution environment domain tests how GitLab selects a runner and what the job actually runs inside: container image, services, checkout strategy, working directory, and runner configuration. Expect scenario questions about tags, protected runners, executor behavior, and environment variables that describe the runtime.
Runner selection and configuration
Runners can be instance, group, or project scoped; shared runners are instance runners made available to projects. A job's tags must match runner tags, and a runner configured not to run untagged jobs will not pick up a job with no tags. Protected runners run only protected branches and tags. Runners authenticate with a runner authentication token; registration tokens are deprecated. In config.toml, concurrent limits global parallelism and limit caps a runner's concurrent jobs.
Executors and job environment
The shell executor runs directly on the host and ignores image; the Docker executor runs each job in a container from image; the Kubernetes executor runs jobs as pods; the instance executor is for autoscaling on cloud instances. Docker services are reachable by alias on the job network, not usually localhost. GIT_STRATEGY controls checkout (clone, fetch, none), and GIT_DEPTH controls history depth. before_script, script, and after_script share job variables, but after_script runs in a separate shell, so cd and exported shell state do not carry over; an after_script failure does not change the job's exit status.
Worked example
test:
image: python:3.12-slim
services:
- name: postgres:16
alias: db
tags: [docker, linux]
variables:
POSTGRES_PASSWORD: test
before_script:
- pip install -r requirements.txt
script:
- pytest --db-host db
The tags choose a Linux Docker runner. The image supplies Python and pip. The service is reachable as db; using localhost would target the job container. before_script installs dependencies in the same shell context as script.
Traps
Assuming a shell executor honors image costs marks. Expecting after_script to run after a cancelled job is wrong; it can be skipped. A cache stored on one runner is not automatically on another. Docker-in-Docker needs privileged mode, which is a security trade-off, not a default.