GitLab Runners are the agents that execute your CI/CD jobs, and getting them registered and configured correctly is fundamental to making anything happen in your pipeline.
Let’s see a runner in action. Imagine this .gitlab-ci.yml file:
stages:
- build
build_job:
stage: build
script:
- echo "Hello from the build stage!"
- echo "This job is running on runner tag: $CI_RUNNER_TAG"
When this pipeline runs, a GitLab Runner picks up the build_job. The runner will then execute the commands specified in the script section, and the output will appear in the pipeline job logs. The $CI_RUNNER_TAG variable, specific to the runner that picked up the job, will be substituted with its actual tag.
The problem GitLab Runners solve is distributed execution. Your GitLab instance (the server) doesn’t have the resources or the desire to build every single project. Instead, it delegates these tasks to separate "runner" machines. These runners can be anything from a small VM on your laptop to a powerful server in a data center, or even Kubernetes pods. They connect to your GitLab instance, poll for jobs they are configured to run, execute them, and report back the results.
Here’s how it works internally:
- Registration: A runner executable needs to be installed on a machine. This executable then registers itself with your GitLab instance using a URL and a registration token. This process creates a unique, authenticated connection between the runner and GitLab.
- Polling: Once registered, the runner continuously polls the GitLab API for jobs that match its configuration (tags, specific projects, etc.).
- Execution: When a job is found, the runner downloads the project’s code, sets up the execution environment (based on its executor type – Docker, shell, Kubernetes, etc.), and runs the commands defined in the
.gitlab-ci.ymlfile. - Reporting: The runner streams job logs back to GitLab in real-time and reports the job’s status (success, failure, canceled).
The key levers you control are:
- Runner Scope: Runners can be Shared (available to all projects on a GitLab instance), Group (available to all projects within a specific group), or Specific (tied to a single project). You choose this during registration or by reconfiguring an existing runner.
- Tags: Tags are labels you assign to runners (e.g.,
docker,aws,node16). You then use these tags in your.gitlab-ci.ymlto specify which runners can pick up a particular job. This is how you ensure a job runs on an environment with the necessary tools or capacity. - Executor: This defines how the job runs. Common executors include:
shell: Runs jobs directly on the runner’s host machine. Simple but can lead to environment conflicts.docker: Runs jobs in isolated Docker containers. Clean and reproducible.kubernetes: Runs jobs as pods in a Kubernetes cluster. Highly scalable and flexible.virtualbox,parallels,docker+machine: For running jobs in virtual machines.
- Concurrency: You can configure how many jobs a single runner can execute simultaneously. This is controlled by the
concurrentsetting in the runner’sconfig.tomlfile.
The one thing most people don’t realize is that the privileged flag in a Docker executor configuration isn’t just about giving the container root access inside itself; it fundamentally changes how the Docker daemon interacts with the container, allowing it to perform operations that typically require direct daemon access, such as mounting volumes or manipulating network interfaces as if it were the daemon itself. This is crucial for tasks like building Docker images within a Docker-in-Docker (DinD) setup.
Once you’ve got your runners registered and tagged, the next logical step is to start optimizing their performance and security, often by exploring advanced executor configurations or implementing caching strategies.