DevOpsLesson
DevOpsLesson

Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.

Stay Updated

Get notified about new tutorials and features.

Tutorials

  • What is DevOps?
  • Docker Tutorial
  • Terraform Tutorial
  • CI/CD Pipeline
  • All Tutorials

Roadmaps

  • DevOps Engineer
  • Cloud Engineer
  • SRE Path
  • All Roadmaps

Company

  • About Us
  • Blog
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 DevOpsLesson. All rights reserved.

DOCKERKUBERNETESTERRAFORMAWSCI/CDLINUXGITDEVOPS ROADMAPCLOUD ROADMAPSRE ROADMAPGIT CHEATSHEETDOCKER CHEATSHEETK8S CHEATSHEETTF CHEATSHEETLINUX CHEATSHEETDOCKERFILE LINTERYAML VALIDATORCRON PARSERREGEX TESTER

Cicd Tutorial

Introduction to CI/CD
Getting Started with GitLab CI/CD
Stages, Jobs, Artifacts & Cache
Variables, Secrets and Environments in GitLab CI
GitLab Runners
GitLab CI Pipeline Rules
GitLab CI Environments
GitLab CI Docker Workflows
GitLab CI Testing
GitLab CI Security Scanning
GitLab CI Deploy to AWS
GitLab CI Multi-Project Pipelines
GitLab CI Best Practices

GitLab Runners

PreviousPrev
Next

Learn what a GitLab Runner is, how GitLab CI runners execute jobs, runner types, executor choices, runner registration, tags, security, concurrency, and a full Docker executor example.

A GitLab Runner is the worker process that actually executes the jobs you define in .gitlab-ci.yml. GitLab itself stores your repository, reads your pipeline definition, calculates which jobs should run, and shows the results in the UI. The runner is the agent that picks up one of those jobs, prepares an execution environment, runs the commands, collects logs, uploads artifacts, and reports the status back to GitLab. If GitLab CI/CD is the brain of your automation system, the runner is the pair of hands doing the work.

This distinction matters because many GitLab beginners assume that adding a job to .gitlab-ci.yml is enough by itself. In reality, GitLab needs available runners. Without a runner, jobs remain stuck in a pending state. That is why understanding GitLab CI runner architecture, GitLab runner types, and GitLab runner registration is essential for building reliable pipelines.

What Is a GitLab Runner and Why It Exists

GitLab pipelines are declarative. You describe stages, jobs, rules, images, artifacts, environments, and deployment behavior in YAML. But YAML does not execute itself. GitLab therefore relies on the GitLab Runner application, which can be installed on Linux, Windows, macOS, virtual machines, cloud hosts, or Kubernetes nodes.

A runner exists for a few practical reasons:

  • Isolation: jobs run in separate environments instead of directly on the GitLab web application server.
  • Scalability: many runners can serve one GitLab instance, so pipelines can run in parallel.
  • Flexibility: each runner can use a different executor such as shell, Docker, Kubernetes, or SSH.
  • Security boundaries: you can restrict which runners handle which projects, branches, or secrets.
  • Specialized infrastructure: you might need GPU machines, private network access, specific operating systems, or licensed tools.

When a pipeline starts, GitLab places jobs into a queue. Runners poll GitLab for matching jobs. A runner only picks up a job if the job matches its scope and tags, and if the runner is allowed to execute it. Once assigned, the runner prepares the job environment, checks out the repository, restores cache if configured, runs the job script, uploads artifacts, and returns either success or failure.

Types of GitLab Runners

When people search for shared runner GitLab, group runner, or specific runner GitLab, they are really asking who a runner belongs to and which projects may use it.

Shared runners

Shared runners are available to many projects. On GitLab.com, shared runners are the easiest way to get started because GitLab hosts and manages them for you. You do not need to install anything. If your project is allowed to use shared runners, your jobs can start immediately.

Shared runners are convenient for:

  • tutorials and practice projects
  • public repositories
  • small internal projects
  • teams that want zero infrastructure maintenance

However, shared runners also come with tradeoffs:

  • you control less of the underlying machine
  • capacity can be shared with other users
  • you should avoid exposing sensitive production secrets to broadly shared infrastructure
  • you cannot easily guarantee access to private internal networks

That is why many companies start with shared runners for development workloads and move critical deployment jobs to self-hosted runners.

Group runners

Group runners are attached at the GitLab group level and can be used by projects within that group. This is a good middle ground between convenience and control.

Use group runners when:

  • several related projects need the same build environment
  • platform teams want to standardize CI for many repositories
  • you want one runner fleet for all services in a business unit
  • you need private infrastructure but do not want per-project duplication

Group runners are especially helpful in monorepos or in organizations with many microservices using the same language stack.

Project-specific runners

A project-specific runner is registered for one project only. This is often the safest and most controlled option.

Use project runners when:

  • one project needs unique software or hardware
  • production deployment access must be tightly restricted
  • compliance rules require stronger separation
  • you want custom tags, concurrency, or executors for one repository only

If your project deploys to production or touches highly sensitive infrastructure, a project-specific runner is often a better design than relying on shared runners.

GitLab Runner Executor Types

The next major concept is the GitLab runner executor. The executor determines how the job runs.

Shell executor

The shell executor runs job commands directly on the host machine using Bash, PowerShell, or another supported shell.

Use shell when:

  • you control the server completely
  • the host already has the required tools installed
  • jobs need direct host access
  • you want the simplest possible setup

Pros:

  • easy to set up
  • fast startup
  • direct host access

Cons:

  • weak isolation compared with containers
  • greater risk of jobs affecting each other
  • tool versions can drift over time

The shell executor is fine for trusted internal jobs, but it is usually not the best default for multi-tenant CI.

Docker executor

The Docker executor is the most popular option. Each job runs inside a container created from the configured image. That gives you clean, repeatable environments and makes language-specific builds straightforward.

Use Docker when:

  • you want reproducible builds
  • each job should start from a clean image
  • different jobs need different toolchains
  • you want better isolation than shell

Pros:

  • clean environment per job
  • easy to select language images like node:20, python:3.12, or golang:1.24
  • portable and consistent

Cons:

  • Docker must be available on the runner host
  • container startup adds some overhead
  • certain advanced workloads need privileged features or extra configuration

Docker+Machine executor

The docker+machine executor creates temporary Docker hosts on demand, traditionally using Docker Machine. Conceptually, this gives you elastic runners that spin up new machines for jobs and tear them down later.

Use it when:

  • workloads are bursty
  • you want autoscaling CI workers in the cloud
  • you want stronger isolation at the VM level

Pros:

  • scalable
  • ephemeral build machines
  • useful for larger self-hosted fleets

Cons:

  • more infrastructure complexity
  • cloud cost management matters
  • more moving parts than plain Docker

In modern GitLab environments, many teams now prefer Kubernetes-based autoscaling rather than older Docker Machine workflows, but the concept is still worth knowing.

Kubernetes executor

The Kubernetes executor launches each CI job as a pod in a Kubernetes cluster.

Use Kubernetes when:

  • your platform already runs on Kubernetes
  • you need elastic scaling
  • you want centralized scheduling and cluster governance
  • your DevOps team is comfortable managing cluster permissions and namespaces

Pros:

  • powerful scaling model
  • good isolation per job
  • aligns with cloud-native platforms

Cons:

  • more complex than shell or Docker
  • requires Kubernetes expertise
  • cluster RBAC and networking must be configured carefully

SSH executor

The SSH executor connects to an external machine over SSH and runs commands there.

Use SSH when:

  • you must run jobs on a remote server that already exists
  • deployment steps need a fixed target host
  • you cannot install a runner locally on that host

Pros:

  • useful for legacy environments
  • works with existing servers

Cons:

  • weaker reproducibility
  • network and credential management become important
  • less common for general build pipelines

How the Docker Executor Works

Because the GitLab runner Docker executor is so widely used, it deserves a closer look. When a job is assigned to a Docker executor runner, the runner performs a sequence like this:

  1. pulls the job image if it is not already present
  2. creates a fresh container for the job
  3. checks out the repository into the job workspace
  4. restores cache or downloads artifacts if configured
  5. runs the before_script, script, and after_script
  6. uploads artifacts, reports, and cache
  7. destroys the container when the job is done

That “fresh container per job” model is the reason Docker executor pipelines are so predictable. If one job installs a package or writes files, those changes do not magically leak into the next job unless you explicitly store them as artifacts or cache. This clean-slate behavior reduces the “works on one runner, fails on another” problem.

For example, if your job uses:

image: node:20
script:
  - npm ci
  - npm test

The Docker executor pulls node:20, starts a container, and runs the Node commands there. Another job in the same pipeline could use python:3.12 with no conflict because it gets a separate container.

Installing GitLab Runner on Ubuntu or Linux

If you want your own runner, installing GitLab Runner on Ubuntu is a common choice. The exact package source may vary by GitLab version, but the typical flow looks like this.

1. Update package indexes

sudo apt-get update
sudo apt-get install -y curl ca-certificates

2. Add the GitLab Runner repository

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash

3. Install the runner package

sudo apt-get install -y gitlab-runner

4. Verify the installation

gitlab-runner --version
sudo gitlab-runner status

If you plan to use the Docker executor, also install Docker on the host and ensure the runner can access the Docker daemon.

sudo apt-get install -y docker.io
sudo systemctl enable --now docker
sudo usermod -aG docker gitlab-runner

Depending on your environment, you may need to restart the service after group membership changes.

Registering a Runner with gitlab-runner register

Installing the binary is not enough. The runner must be registered with a GitLab instance so GitLab knows which runner belongs to which scope.

A standard interactive registration looks like this:

sudo gitlab-runner register

You are typically prompted for:

  • GitLab instance URL, such as https://gitlab.com/
  • registration token or authentication details from GitLab
  • runner description
  • runner tags
  • executor type
  • default Docker image if using Docker executor

An example interactive session might look like:

Enter the GitLab instance URL:
https://gitlab.com/

Enter the registration token:
glrt-xxxxxxxxxxxxxxxx

Enter a description for the runner:
prod-docker-runner

Enter tags for the runner:
docker,production

Enter an executor:
docker

Enter the default Docker image:
alpine:3.22

You can also do non-interactive registration in automation scripts:

sudo gitlab-runner register   --non-interactive   --url "https://gitlab.com/"   --token "glrt-xxxxxxxxxxxxxxxx"   --description "docker-build-runner"   --tag-list "docker,build"   --executor "docker"   --docker-image "alpine:3.22"

This is useful when provisioning runners with Infrastructure as Code.

Understanding config.toml

After registration, GitLab Runner stores its configuration in config.toml, commonly located at:

/etc/gitlab-runner/config.toml

A simplified example looks like this:

concurrent = 2
check_interval = 0

[[runners]]
  name = "docker-build-runner"
  url = "https://gitlab.com/"
  token = "glrt-xxxxxxxxxxxxxxxx"
  executor = "docker"
  tags = ["docker", "build"]

  [runners.docker]
    tls_verify = false
    image = "alpine:3.22"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache"]
    shm_size = 0

Important fields include:

  • concurrent: maximum number of jobs the runner process can execute at once across all configured runners
  • [[runners]]: one runner definition block
  • name: human-friendly label
  • url: GitLab instance URL
  • token: runner authentication token
  • executor: shell, docker, kubernetes, and so on
  • tags: routing labels used by jobs
  • [runners.docker]: Docker-specific settings such as default image and privileged mode

In real environments, config.toml can contain additional settings for cache backends, Kubernetes namespaces, session servers, custom volumes, pull policies, and network settings.

Using Tags to Route Jobs

GitLab runner tags are one of the most practical controls in CI/CD. Tags let you ensure the right jobs go to the right runners.

Suppose you have two runners:

  • one tagged docker
  • one tagged production

Then you can route a job like this:

build-image:
  stage: build
  tags:
    - docker
  script:
    - docker build -t myapp .

And a production deployment job like this:

deploy-prod:
  stage: deploy
  tags:
    - production
  script:
    - ./deploy-prod.sh

Tags are critical when you have specialized runners for:

  • Docker builds
  • Kubernetes deployments
  • private network access
  • GPU workloads
  • production-only tasks

Without tags, jobs may land on any eligible runner, which can cause failures or security problems.

Runner Security Best Practices

Security is where many teams underestimate the impact of runner design. A runner can see repository code, job logs, variables, artifacts, and possibly deployment credentials. Treat runners as trusted infrastructure, not disposable trivia.

Why not use shared runners for production secrets?

Shared runners are convenient, but they are not ideal for highly sensitive production credentials. Even when GitLab isolates jobs, a broad shared environment increases your risk surface. For production deployments, it is safer to use self-hosted runners that you control, ideally scoped to one group or one project and protected by tags.

Protected runners

A protected runner only executes jobs from protected branches or protected tags. This prevents an untrusted feature branch from using a runner that has access to sensitive environments.

Example use case:

  • main and release tags are protected
  • production runner is marked protected
  • only approved maintainers can trigger deploys using that runner

Protected variables

A protected variable is only exposed to jobs running on protected refs. This pairs naturally with protected runners.

Good pattern:

  • production cloud credentials stored as protected masked variables
  • deploy job runs only on protected branches
  • deploy job is tagged to a protected self-hosted runner

That combination is far safer than placing production credentials in jobs that can run on any shared runner.

Runner Concurrency Settings

The concurrent setting in config.toml controls how many jobs the runner process may execute simultaneously. If concurrent = 4, up to four jobs can run at the same time, subject to machine resources and runner configuration.

Concurrency is a balancing act:

  • higher concurrency improves throughput if CPU, memory, and disk can handle it
  • too much concurrency causes slow builds, timeouts, or resource contention
  • too little concurrency leaves pipelines waiting unnecessarily

On a small VM, concurrent = 1 or 2 may be appropriate. On a larger build host with plenty of CPU and memory, higher values might be fine. Monitor actual job performance before increasing it aggressively.

Complete Example: Docker Executor Runner and Matching Pipeline

Here is a practical end-to-end example.

Register the runner

sudo gitlab-runner register   --non-interactive   --url "https://gitlab.com/"   --token "glrt-xxxxxxxxxxxxxxxx"   --description "team-docker-runner"   --tag-list "docker,team-a"   --executor "docker"   --docker-image "node:20"

Resulting runner intent

  • executor: Docker
  • default image: node:20
  • tags: docker, team-a
  • scope: project or group depending on where you registered it

.gitlab-ci.yml

stages:
  - test
  - build

unit-tests:
  stage: test
  tags:
    - docker
    - team-a
  image: node:20
  script:
    - npm ci
    - npm test

build-app:
  stage: build
  tags:
    - docker
    - team-a
  image: node:20
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/

What happens here?

  1. GitLab creates the pipeline.
  2. The registered runner polls GitLab and sees jobs matching docker and team-a.
  3. For each job, the Docker executor launches a fresh node:20 container.
  4. The test job runs first.
  5. The build job runs next and uploads dist/ as an artifact.

This is the core of how GitLab CI runner, GitLab runner Docker executor, and GitLab runner tags work together.

Common Mistakes to Avoid

A few common problems cause runner confusion:

  1. No matching tags - the job requires tags that no runner has.
  2. Runner not online - the service is stopped or cannot reach GitLab.
  3. Wrong executor choice - for example, using shell when the job assumes containers.
  4. Too many secrets on shared runners - convenient at first, risky later.
  5. Overloading concurrency - the host becomes the bottleneck.
  6. Missing Docker permissions - the runner host can run GitLab Runner, but not Docker commands.

Final Takeaway

A GitLab Runner is the engine that turns CI/CD configuration into actual execution. Once you understand runner scope, executor types, tags, registration, and security boundaries, your pipelines become much easier to design and troubleshoot. For most teams, the Docker executor is the best starting point because it offers clean isolation, reproducible jobs, and excellent compatibility with modern CI workflows. As your platform grows, group runners, protected runners, and carefully chosen concurrency settings help you scale without losing control.


Knowledge Check

Exercise

Question 1: Runner Purpose

What is the primary job of a GitLab Runner in GitLab CI/CD?

Exercise

Question 2: Executor Choice

Why is the Docker executor a popular default for GitLab CI runners?

Exercise

Question 3: Security and Tags

Which setup is safest for production deployment jobs that use sensitive credentials?

PreviousPrev
Next

Continue Learning

GitLab CI Artifacts

Learn GitLab CI artifacts, how to define, use, and download build artifacts, pass files between jobs, set expiration times, and use artifacts for test reports and deployment packages.

10 min read·Easy

GitLab CI Cache

Speed up your GitLab CI pipelines with caching, learn how to define cache paths, set cache keys, understand cache policies, and cache node_modules, pip packages, and Maven dependencies.

10 min read·Easy

Variables, Secrets and Environments in GitLab CI

Master GitLab CI/CD variables, predefined variables, custom variables, masked and protected secrets, group-level variables, environment-scoped variables, and .env file injection.

12 min read·Easy

Explore Related Topics

Do

Docker Tutorials

Build and push Docker images in your pipelines

Gi

Git Tutorials

Trigger CI/CD pipelines from Git events

Try the Tool

YAML Validator

Validate GitHub Actions and CI/CD pipeline YAML files.

Cron Parser

Build and understand scheduled workflow cron expressions.

On This Page

What Is a GitLab Runner and Why It ExistsTypes of GitLab RunnersShared runnersGroup runnersProject-specific runnersGitLab Runner Executor TypesShell executorDocker executorDocker+Machine executorKubernetes executorSSH executorHow the Docker Executor WorksInstalling GitLab Runner on Ubuntu or Linux1. Update package indexes2. Add the GitLab Runner repository3. Install the runner package4. Verify the installationRegistering a Runner with `gitlab-runner register`Understanding `config.toml`Using Tags to Route JobsRunner Security Best PracticesWhy not use shared runners for production secrets?Protected runnersProtected variablesRunner Concurrency SettingsComplete Example: Docker Executor Runner and Matching PipelineRegister the runnerResulting runner intent`.gitlab-ci.yml`Common Mistakes to AvoidFinal TakeawayKnowledge Check