GitLab Runner Architecture, Registration, Runner Managers, Job Execution, and Lifecycle: Guided Hands-On Workflow and Core Operations
Turn the runner model into controlled evidence. In a disposable project you will inspect existing capacity, create one narrowly scoped runner configuration, register one isolated runner manager with the current authentication-token workflow, route a uniquely tagged trusted job, observe status/version/system identity, pause and stop execution deliberately, then unregister and delete only the disposable resources you created.
Learning objectives
- Inspect existing runners without exposing tokens or changing runner policy.
- Create one disposable project runner using the modern creation workflow and register one isolated manager with a runner authentication token.
- Route a trusted synthetic job exclusively to that runner by using a unique tag and verify the assigned runner from job evidence.
- Demonstrate the operational difference among pausing the runner, stopping the manager process, resuming service, and unregistering the manager.
- Delete the disposable runner configuration and local runner storage only after proving no required jobs or evidence depend on it.
1. Preflight: prove the project and local runtime are disposable
LAB_TAG="ch04-lab"
RUNNER_NAME="ch04-disposable-manager"
RUNNER_CONTAINER="ch04-runner-manager"
RUNNER_VOLUME="ch04-runner-config"
EVIDENCE="ch04-evidence"
mkdir -p "$EVIDENCE"
git status --short --branch
git rev-parse HEAD | tee "$EVIDENCE/head-before.txt"
docker version --format '{{.Server.Version}}'
docker ps -a --filter "name=^/${RUNNER_CONTAINER}$"
docker volume ls --filter "name=^${RUNNER_VOLUME}$"
If either exact Docker resource already exists and was not created for this lab, stop. Choose new disposable names rather than deleting unknown infrastructure. In GitLab, inspect Settings → CI/CD → Runners and record existing runner IDs/descriptions without copying any token.
2. Create one narrowly scoped project runner in GitLab
In the disposable project, open Settings → CI/CD → Runners → Create project runner. Use these deliberate settings:
-
Description:
ch04-disposable-runner. - Tags: exactly
ch04-lab. - Run untagged: disabled.
- Protected: disabled for this disposable unprotected lab branch; do not alter a production protection policy for the exercise.
- Scope: project only.
After creation, record the runner ID and routing settings. GitLab displays a runner authentication token for registration. Put it into a temporary shell variable or secure prompt input; never echo, paste into notes, or commit it.
# Example only: assign the secret in your local shell without printing it.
read -rsp "Runner authentication token: " RUNNER_AUTH_TOKEN
printf '
'
test -n "$RUNNER_AUTH_TOKEN"
# Do not run: echo "$RUNNER_AUTH_TOKEN"
3. Register one isolated manager with Runner v19.3.1
docker volume create "$RUNNER_VOLUME"
docker run --rm \
-v "$RUNNER_VOLUME:/etc/gitlab-runner" \
gitlab/gitlab-runner:v19.3.1 register \
--non-interactive \
--url "https://gitlab.com/" \
--token "$RUNNER_AUTH_TOKEN" \
--executor "shell" \
--description "$RUNNER_NAME"
unset RUNNER_AUTH_TOKEN
docker run -d \
--name "$RUNNER_CONTAINER" \
--restart no \
-v "$RUNNER_VOLUME:/etc/gitlab-runner" \
gitlab/gitlab-runner:v19.3.1 run
This lab intentionally uses a Shell executor inside a disposable runner container for a synthetic trusted job and does not mount the host Docker socket. Shell executor is high risk on a shared/persistent host; Chapter 05 compares executors in depth. The container here is a disposable teaching boundary, not a recommendation for production runner architecture.
4. Verify registration without reading the token
docker exec "$RUNNER_CONTAINER" gitlab-runner --version
docker exec "$RUNNER_CONTAINER" gitlab-runner list
docker exec "$RUNNER_CONTAINER" gitlab-runner verify
In the GitLab runner UI, verify the runner is no longer “never contacted” and inspect its manager details if available: system ID, version, platform/architecture, contacted time, status, and job execution state. Save screenshots only if they do not reveal authentication material.
5. Route one trusted job exclusively to the lab runner
stages: [inspect]
runner_identity_probe:
stage: inspect
tags:
- ch04-lab
script:
- printf 'pipeline_id=%s
' "$CI_PIPELINE_ID"
- printf 'job_id=%s
' "$CI_JOB_ID"
- printf 'sha=%s
' "$CI_COMMIT_SHA"
- printf 'runner_id=%s
' "$CI_RUNNER_ID"
- printf 'runner_description=%s
' "$CI_RUNNER_DESCRIPTION"
- printf 'runner_tags=%s
' "$CI_RUNNER_TAGS"
- printf 'runner_version=%s
' "$CI_RUNNER_VERSION"
- printf 'runner_revision=%s
' "$CI_RUNNER_REVISION"
- printf 'runner_arch=%s
' "$CI_RUNNER_EXECUTABLE_ARCH"
- printf 'sha=%s
runner=%s
' "$CI_COMMIT_SHA" "$CI_RUNNER_ID" > runner-evidence.txt
artifacts:
expire_in: 1 day
paths:
- runner-evidence.txt
Because the tag is unique and “run untagged” is disabled, the expected job must match this runner’s routing policy. After execution, correlate pipeline/job/SHA and runner metadata; do not infer routing only from a green status.
6. Pause the runner and observe scheduling policy
Pause the disposable project runner in the GitLab UI. Do
not stop the local manager yet. Trigger another
trusted job on the same disposable branch. With no other runner
carrying the unique ch04-lab tag, the job should remain
pending because the runner configuration is deliberately ignoring
new jobs.
Record the pending job ID and the runner’s paused state. Then resume the runner. The same queued job should become eligible without changing its YAML. This separates GitLab scheduling policy from repository configuration.
7. Stop the manager process and distinguish offline from paused
docker stop "$RUNNER_CONTAINER"
docker ps -a --filter "name=^/${RUNNER_CONTAINER}$"
The runner configuration still exists and is no longer paused, but the manager stops contacting GitLab and becomes offline after the normal status interval. Queue another uniquely tagged job and preserve its pending state. Then restart only the known container:
docker start "$RUNNER_CONTAINER"
docker logs --tail 80 "$RUNNER_CONTAINER"
When contact resumes, verify the pending job is claimed. The experiment proves that “unpaused” and “online manager” are separate conditions for execution.
8. Unregister the manager without confusing it with runner deletion
docker exec "$RUNNER_CONTAINER" gitlab-runner list
docker exec "$RUNNER_CONTAINER" \
gitlab-runner unregister --name "$RUNNER_NAME"
docker exec "$RUNNER_CONTAINER" gitlab-runner list
With the modern authentication-token workflow, unregistering removes the manager registration/local configuration entry; the GitLab-side runner configuration can remain. Verify that distinction in the UI. Do not repeat registration merely to make the UI disappear.
9. Delete only the disposable runner configuration, then local storage
After confirming no useful jobs depend on the runner, remove the disposable runner configuration through the GitLab UI. Then remove only the exact local lab resources:
docker rm -f "$RUNNER_CONTAINER" 2>/dev/null || true
docker volume inspect "$RUNNER_VOLUME" >/dev/null 2>&1 && \
docker volume rm "$RUNNER_VOLUME"
docker ps -a --filter "name=^/${RUNNER_CONTAINER}$"
docker volume ls --filter "name=^${RUNNER_VOLUME}$"
The final listing should show no lab container/volume. Keep non-secret evidence such as runner ID, manager system ID/version, pipeline/job IDs, and timestamps; do not retain the authentication token.
10. Challenge: classify state before changing it
| Observation | Correct first interpretation | Do not do first |
|---|---|---|
| Runner is paused but manager is online | GitLab policy is blocking new jobs. | Reinstall Runner. |
| Runner unpaused, manager offline | Manager/service/network is unavailable. | Edit job script. |
| Manager online, job requests missing extra tag | Eligibility mismatch. | Rotate token. |
| Job ran on wrong broad runner | Routing/scope policy is too permissive. | Add retries. |
| Auth token appears in a log | Credential exposure incident. | Assume masking later fixes it. |
Knowledge check
Why disable “run untagged” on the disposable capability runner?
It prevents unrelated untagged jobs from drifting onto a runner intended only for explicitly tagged lab work.
What does pausing prove that stopping the manager does not?
Pausing proves GitLab-side scheduling policy can block new jobs while the manager process may remain online.
What does stopping the manager prove?
The runner configuration can remain valid/unpaused while execution stops because no manager is contacting GitLab.
Why does the job print CI_RUNNER_ID and version
metadata but not runner tokens?
Those values are operational evidence; the authentication token is a secret credential and is unnecessary for proving routing.
After gitlab-runner unregister in the modern
workflow, why can the runner still appear in GitLab?
Unregistering can remove only that runner manager. The runner configuration is a separate reusable GitLab-side object and must be deleted separately when retirement is intended.
Official references and version notes
- Runners — runner categories, job scheduling, GitLab-hosted versus self-managed runners, and execution flow.
- Manage runners — project/group/instance scope, creation workflow, ownership and pause/resume operations.
-
Configure runners
— tags,
run_untagged, protected runners, authentication-token rotation, and routing behavior. - Registering runners and new runner creation workflow — current runner authentication-token registration and deprecated legacy registration-token behavior.
-
GitLab Runner commands
—
register,list,verify,run,stop, andunregisterlifecycle commands. -
Runner fleet planning
— manager
system_ididentity and modern unregister/delete distinctions. - Runners API — runner details, managers, status, pause, job history, authentication-token reset, and deletion semantics.
- Security for self-managed runners — remote-code-execution trust, Shell executor risk, persistent-runner residue, isolation, and credential exposure.
- GitLab Runner documentation — current compatibility guidance recommends keeping Runner major.minor aligned with GitLab; GitLab.com users should keep self-managed runners current.
Version-sensitive statements were rechecked against current
primary GitLab documentation and the GitLab Runner release history
on 2026-09-11. The latest stable Runner tag
visible in the upstream release history at that verification point
is v19.3.1 (2026-08-24); GitLab 19.4 is scheduled
after this guide-authoring date, so executable examples pin
gitlab/gitlab-runner:v19.3.1 instead of a moving
latest tag. The legacy runner-registration-token
workflow is deprecated and scheduled for removal in GitLab 20.0;
this chapter uses runner authentication tokens and the modern
creation 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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.