Chapter 14Lesson 01~230 minutes

GitLab Runners, Executors, Tags, Registration, Autoscaling, Isolation, and Security: Concepts, Architecture, and Mental Model

Model GitLab Runner as remote-code-execution infrastructure: runner scope, eligibility, authentication, executors, isolation, autoscaling, persistence, and job identity.

GitLab RunnerExecutorsTagsScopesAuthenticationIsolation

Learning objectives

  • Explain the control-plane/job-queue/runner-manager/executor boundary and why Runner is remote-code-execution infrastructure.
  • Distinguish instance, group, and project runner scope from tags, protected state, paused state, lock state, and runner health status.
  • Explain the modern runner authentication-token registration workflow and why legacy registration-token instructions are obsolete.
  • Compare Docker, Kubernetes, autoscaling, instance, shell, SSH, and custom executor isolation/persistence characteristics.
  • Reason about runner eligibility before a job starts and identify which control is responsible when no runner can pick up a job.
Availability baseline (verified 2026-08-21 against current GitLab documentation). GitLab Runner, project/group/instance runner scopes, tags, protected runners, the supported executor framework, and self-managed runners are available across Free/Premium/Ultimate. GitLab-hosted runners are a GitLab.com service and are also available for GitLab Dedicated under a separately provisioned Limited Availability offering; Self-Managed installations provide their own runner infrastructure. Hosted-runner compute can be quota/billing constrained, so every mandatory exercise has a no-runner/static or evidence-fixture path. Legacy runner registration tokens are deprecated and scheduled for removal in GitLab 20.0; this chapter teaches the modern runner creation workflow with runner authentication tokens.

1. The problem: the pipeline is only as trustworthy as the compute that runs it

Chapter 13 ended at the point where GitLab has resolved a job's configuration, variables, permissions, and ref context. None of that code executes inside the GitLab web interface. A GitLab Runner process asks GitLab for eligible jobs and hands each accepted job to an executor. That executor might run commands directly on a persistent host, inside a container, in a Kubernetes pod, or on a newly provisioned ephemeral virtual machine.

This makes a runner a security boundary. Repository-controlled scripts can read source code, use the job token, receive eligible variables, write caches and artifacts, and reach whatever network endpoints the execution environment allows. A runner is therefore not a passive status reporter; it is remote-code-execution infrastructure.

2. Mental model: GitLab schedules; Runner executes

Control plane and execution plane
flowchart TD
  C[Commit / MR / pipeline source] --> G[GitLab CI/CD control plane]
  G --> Q[Pending job queue]
  R[Runner manager
authentication token] -->|polls for eligible work| Q
  Q -->|job payload + job token| R
  R --> E{Executor}
  E --> D[Docker container]
  E --> K[Kubernetes pod]
  E --> S[Shell on host]
  E --> A[Ephemeral autoscaled instance]
  D --> O[Logs / artifacts / cache]
  K --> O
  S --> O
  A --> O

GitLab owns pipeline creation, job metadata, permissions, and the pending queue. The runner manager authenticates itself to GitLab, receives an eligible job, and creates the execution environment. The job receives job-scoped identity such as CI_JOB_TOKEN; the runner authentication token belongs to runner configuration and must not be exposed to job scripts.

3. Runner identity is not job identity

Identity Purpose Lifetime/location Do not confuse it with
Runner authentication token Authenticates the runner configuration/manager to GitLab when requesting jobs. Stored on the runner host in config.toml; rotate/revoke when compromised. A token that job scripts should print or consume.
Runner manager system ID Distinguishes a machine/process using a reusable runner configuration. Generated by Runner; visible as runner-manager metadata. A project member identity.
Job token Authorizes the running job to the limited GitLab resources allowed for that job/user/project context. Delivered for the job and expires/revokes with job lifecycle. The runner authentication token.
User identity Contributes permissions to pipeline creation and some job actions. GitLab account/session/token. The OS account that executes commands on a runner host.

If an attacker steals a runner authentication token, they may be able to clone/impersonate runner capacity. If a malicious job receives a job token, its GitLab access is bounded by the job-token model, but the job can still attack the runner host or network if isolation is weak.

4. Runner scope: where a runner can be considered

Scope Where jobs may come from Typical owner Current creation prerequisite
Instance runner Projects across the GitLab instance, subject to project/instance policy. Platform/instance team. Administrator for Self-Managed instance-runner creation.
Group runner Projects in a group and its subgroups where the runner is available. Group/platform team. Group Owner to create; Maintainer/Owner can view group runners.
Project runner Explicitly assigned project(s). Project/platform team. Maintainer to create a project runner; sharing to other projects requires permission in both sides.

Scope answers which projects can even consider this runner. It does not guarantee the runner can execute a particular job. Tags, protected status, paused state, runner health, project enablement, and capacity still apply.

5. Job-to-runner eligibility is an intersection, not a single switch

A useful beginner model is:

eligible = in_scope
        AND runner_accepts_new_jobs
        AND runner_is_available_to_project
        AND runner_has_every_job_tag
        AND untagged_policy_matches
        AND protected_ref_policy_matches
        AND runner_manager_is_online
        AND execution_capacity_is_available

GitLab's documented tag rule is strict: if a job lists multiple tags, a runner must have all of them. A runner may have extra tags. An untagged job additionally requires a runner configured to run untagged jobs. CI/CD runner tags are unrelated to Git tags.

6. Paused, locked, protected, and status mean different things

Control/state Meaning Effect on eligibility
Paused Runner ignores new jobs. Directly prevents pickup of new work.
Locked to current projects Project-runner sharing control. The runner cannot be enabled for additional projects. Does not by itself pause the runner or filter refs; limits future project assignment.
Protected Runner is reserved for protected branches/tags, with current MR-policy nuances. Filters jobs by protected-ref policy.
online / offline / stale / never_contacted Observed runner-manager contact status. Offline/stale/never-contacted capacity cannot execute a job now.
Run untagged jobs Whether a runner may accept jobs whose YAML has no tags. Filters untagged jobs independently of tag list.

Do not use “locked” as a synonym for “protected,” and do not interpret “online” as proof that a runner matches a job's tags or scope.

7. Modern registration: create first, then authenticate the manager

The current workflow separates runner creation in GitLab from runner-manager registration on the machine. Creating the runner records ownership, scope, tags, protected/run-untagged policy, and other GitLab-side metadata. GitLab then shows a runner authentication token for registration.

# Conceptual only — never paste a real runner token into course notes or logs.
# 1) Create the project/group/instance runner in GitLab.
# 2) On the isolated runner host, keep the token out of shell history:
read -rsp "Runner authentication token: " RUNNER_AUTH_TOKEN; echo
sudo gitlab-runner register \
  --non-interactive \
  --url "https://gitlab.com/" \
  --token "$RUNNER_AUTH_TOKEN" \
  --executor "docker" \
  --docker-image "alpine:3.22"
unset RUNNER_AUTH_TOKEN

Legacy --registration-token workflows are deprecated and scheduled for removal in GitLab 20.0. On modern instances the old workflow may already be disabled and can return 410 Gone - runner registration disallowed. The correct repair is migration to the runner creation/authentication-token workflow, not re-enabling legacy registration merely to keep an old tutorial working.

8. Executors: where the job actually runs

Executor Execution environment Isolation/persistence lesson Current lifecycle note
Docker Container on a Runner host. Usually cleaner isolation than shell when non-privileged; host mounts/privileged mode can collapse that boundary. Actively supported; supports image and services.
Kubernetes Pods in a Kubernetes cluster. Per-job pods can isolate filesystems, but cluster RBAC, service accounts, nodes, privileged containers, and network policy define real blast radius. Actively supported.
Docker Autoscaler Docker jobs on autoscaled instances via fleeting/taskscaler. Can use one job per ephemeral instance to reduce persistence between jobs. Generally available since Runner 17.1.
Instance Job runs directly on autoscaled host instance. Useful when container execution is unsuitable; host is still the job boundary. Autoscaling executor using fleeting/taskscaler.
Shell Commands execute directly on runner machine. High host/network persistence risk; use only for trusted builds with a deliberately hardened host. Currently in maintenance mode.
SSH / Custom Remote machine over SSH / custom driver. Isolation depends on remote/custom implementation and cleanup. Currently in maintenance mode.
Docker Machine Autoscaled Docker machines. Historical autoscaling model. Deprecated; migrate to current autoscaler/instance patterns.

9. GitLab-hosted runners versus self-managed runners

On GitLab.com, GitLab-hosted runners are integrated instance runners and each normal hosted job runs in newly provisioned infrastructure. They reduce host-maintenance burden, but compute availability, machine type, platform, and quota remain service constraints. Self-managed runners give you executor, network, cache, image, and locality control—but you own patching, isolation, token protection, decommissioning, and capacity.

Dedicated note. Hosted runners for GitLab Dedicated are currently documented as an Ultimate, Limited Availability offering that requires separate provisioning/purchase. Do not design a mandatory course lab around them.

10. Autoscaling changes persistence, latency, and cost—not just capacity

Autoscaled runner fleet
flowchart TD
  Q[Pending jobs] --> M[Runner manager]
  M --> T[Taskscaler]
  T --> F[Fleeting provider plugin]
  F --> I1[Ephemeral instance A]
  F --> I2[Ephemeral instance B]
  I1 -->|one or bounded jobs| X1[Executor]
  I2 -->|one or bounded jobs| X2[Executor]
  X1 --> C[Cleanup / destroy]
  X2 --> C

Autoscaling can reduce cross-job persistence if instances are short-lived. It also introduces provisioning latency, cloud API failure modes, idle-capacity cost, quota limits, and image/bootstrap dependencies. A secure configuration often favors bounded reuse—such as one job per instance for high-risk workloads—over maximizing reuse.

11. The runner boundary includes cache, storage, and network

  • Workspace: persistent shell hosts can retain source or generated files if cleanup is incomplete.
  • Cache: shared or weakly keyed caches can become a cross-job/cross-branch poisoning surface.
  • Container volumes: mounting host sockets or sensitive directories can bypass container isolation.
  • Network: a job can reach anything the runner environment can route to unless segmentation/policy blocks it.
  • Cloud metadata: runner networks should restrict metadata endpoints when jobs do not need them.
  • Runner configuration: config.toml contains runner authentication material and must not be exposed to jobs or artifacts.

12. Read-only inspection before any runner change

Start in Settings → CI/CD → Runners for a project or Build → Runners for a group. Record runner ID/description, type/scope, tags, whether it runs untagged jobs, protected state, pause state, status, and version if visible. Do not copy authentication tokens or runner configuration.

# Use a token already configured for glab; do not print it.
PROJECT_ID="12345678"   # disposable example ID

glab api "projects/$PROJECT_ID/runners" \
  --paginate \
  --jq '.[] | {id, description, runner_type, status, paused, tag_list}'

# If your role permits reading a specific runner:
glab api "runners/42" \
  --jq '{id, description, runner_type, status, paused, tag_list, run_untagged, access_level}' 

The API shape is evidence, not a reason to automate changes immediately. First decide which eligibility dimension you are testing.

13. DevOps connection: runner governance is execution governance

A protected variable does not help if unreviewed code can execute on a privileged persistent runner that has internal credentials mounted on the host. Conversely, a carefully isolated ephemeral runner cannot compensate for granting every project broad access to a deployment credential. Production governance composes source trust + pipeline policy + variable policy + runner eligibility + executor isolation + network/storage policy.

14. Common misconceptions

  • “Online means eligible.” No: tags, scope, protection, pause state, and capacity still matter.
  • “Docker means safe.” Not automatically: privileged mode, host socket mounts, host PID/network modes, or dangerous volumes can defeat isolation.
  • “A project runner is always single-project.” It can be enabled for additional projects unless locked/assignment policy prevents that.
  • “Runner tags are security labels.” They are routing selectors. Security comes from ownership, protection, executor isolation, and project trust.
  • “The old registration token tutorial is still equivalent.” It is deprecated and may be disabled; use runner authentication tokens.

Knowledge check

A runner has tags docker,linux,gpu. Can it execute a job tagged docker,linux?

What does “Lock to current projects” do?

Why is a Shell executor high risk on a shared persistent host?

What token should a job normally use to access allowed GitLab resources?

Why can an online runner still leave a job pending?

What is the recommended registration model in current GitLab?

Summary

Runner design starts with one principle: a runner executes repository-controlled code. Scope determines which projects can consider it; eligibility filters which jobs it can accept; the executor defines isolation; persistence, network, cache, and credentials define blast radius. Modern runner registration separates GitLab-side runner creation from runner-manager authentication and makes ownership auditable.

Official references

Next lesson

Inspect, route, break, and repair runner eligibility

Lesson 2 uses a disposable project to inspect available runners, run the smallest safe hosted job when compute exists, create a deliberate tag mismatch, and optionally register one isolated local runner using the modern token workflow.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.