Checkpoint Lab — GitLab Runners, Executors, Tags, Registration, Autoscaling, Isolation, and Security
Map job-to-runner eligibility in a disposable project, capture one intentional pending-job failure, optionally prove an isolated local runner path, and verify complete runner/config cleanup.
Learning objectives
- Create a job-to-runner eligibility matrix before executing any pipeline.
- Prove one successful or statically validated free-compatible route and one intentionally pending tag-mismatch route.
- Explain the exact eligibility predicate responsible for the pending job using two independent evidence views.
- Optionally register one isolated project runner and prove that only the tagged synthetic job routes to it.
- Clean up runner manager/configuration, GitLab runner records, synthetic refs, and pipeline configuration with verifiable evidence.
1. Checkpoint mission
You are responsible for a small disposable project with two classes of CI work:
- a harmless general verification job that may use an existing hosted/untagged runner; and
-
a capability-specific job that must never run unless a runner
explicitly advertises
ch14-isolated.
Your job is to predict which runner is eligible, prove the selection logic, deliberately create one pending condition, repair it without weakening a runner, and produce a credential-free evidence packet. Registering your own runner is optional.
2. Preflight and assumptions
| Item | Requirement |
|---|---|
| Project |
Disposable project only; recommended
gitlab-ch14-checkpoint.
|
| Tier | Free mandatory path. No Premium/Ultimate feature required. |
| Offering | GitLab.com easiest for hosted-runner path; Self-Managed/Dedicated use visible runner capacity or the fixture path. |
| Role | Developer for branch/pipeline work; Maintainer if creating optional project runner. |
| Runner host | Optional only: disposable isolated VM/container host with no production credentials/network. |
| Compute | Tiny jobs; if quota/capacity unavailable, use CI Lint + runner inventory + expected-state evidence. |
| Secrets | None. Runner auth token, if used, is handled interactively on the runner host and never recorded. |
3. Capture baseline runner inventory
Before editing YAML, capture two independent views:
- UI: Settings → CI/CD → Runners.
- API: project-visible runner metadata.
PROJECT_ID="12345678"
glab api "projects/$PROJECT_ID/runners" --paginate \
--jq '.[] | {id, description, runner_type, status, paused, tag_list}'
Record runner IDs and tags, but no tokens. If the project shows zero usable runners, mark the successful execution step as “static/no-compute” and continue.
4. Write the prediction ledger before the pipeline
| Job | Requested tags | Expected candidate | Prediction |
|---|---|---|---|
general_probe |
none | Any visible runner configured for untagged work. | Runs if such a runner/capacity exists; otherwise remains pending for an understood reason. |
isolated_probe |
ch14-isolated |
Only a runner containing that tag. | Pending initially because baseline inventory intentionally lacks the tag. |
Also predict two state changes: the push creates a pipeline bound to
the new commit SHA; the mismatched job enters
pending without executing its script.
5. Build the smallest checkpoint configuration
stages: [verify]
general_probe:
stage: verify
script:
- printf 'probe=general\n'
- printf 'source=%s\n' "$CI_PIPELINE_SOURCE"
- printf 'sha=%s\n' "$CI_COMMIT_SHA"
- printf 'runner_id=%s\n' "$CI_RUNNER_ID"
- printf 'runner_tags=%s\n' "$CI_RUNNER_TAGS"
isolated_probe:
stage: verify
tags: [ch14-isolated]
script:
- printf 'probe=isolated\n'
- printf 'sha=%s\n' "$CI_COMMIT_SHA"
- printf 'runner_id=%s\n' "$CI_RUNNER_ID"
The configuration prints only safe pipeline/runner metadata. It does not dump all environment variables, tokens, host mounts, network routes, or runner configuration.
6. Validate, commit, and bind evidence to SHA
Use CI Lint/Pipeline Editor first. Then:
git switch -c ch14/checkpoint
git add .gitlab-ci.yml
git diff --cached --check
git diff --cached
git commit -m "ch14: runner eligibility checkpoint"
CHECKPOINT_SHA="$(git rev-parse HEAD)"
printf 'checkpoint_sha=%s\n' "$CHECKPOINT_SHA"
git push -u origin ch14/checkpoint
Record pipeline ID/source/ref/SHA. If
general_probe runs, record the runner ID and compare it
to the baseline inventory. If it remains pending because no compute
is available, document that as a capacity/availability observation
rather than changing security controls.
7. Prove the intentional pending job from two views
View 1 — job: isolated_probe requests
ch14-isolated and is pending.
View 2 — runner inventory: no available runner
contains that tag. The two views establish causality without
executor logs.
# Read-only job/pipeline discovery pattern; exact IDs come from your disposable run.
glab ci status
# Or inspect through the GitLab UI: Build > Pipelines > pipeline > isolated_probe.
Do not “fix” this checkpoint by adding the tag to a broad production runner. The expected result is a controlled failure.
8. Free-compatible repair without registering anything
For the mandatory path, repair the synthetic configuration by
removing the impossible tag from isolated_probe or
converting it to a fixture-only/manual example. Commit the change as
a new commit so the failed pipeline remains auditable.
git add .gitlab-ci.yml
git diff --cached --check
git commit -m "ch14: repair synthetic runner selector"
REPAIR_SHA="$(git rev-parse HEAD)"
printf 'repair_sha=%s\n' "$REPAIR_SHA"
git push
Prediction: both jobs now share the same untagged runner
eligibility. Verification: the repaired pipeline is bound to
REPAIR_SHA, not the original failed SHA.
9. Optional extension: satisfy the selector with isolated capacity
-
Create a project runner in the disposable project with tag
ch14-isolated, Run untagged disabled, and a clear lab-only description. - On the isolated host, register it with the runner authentication token and Docker executor.
-
Run
sudo gitlab-runner verifywithout printingconfig.toml. -
Restore the original two-job checkpoint YAML. Predict that
isolated_proberoutes only to this runner. -
Verify the job's
CI_RUNNER_IDmatches the new runner record.
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
sudo gitlab-runner verify
10. Protection prediction without weakening a runner
Use a written fixture if you do not own a second disposable runner:
| Runner | Protected? | Tags | Job ref | Eligible? |
|---|---|---|---|---|
| R1 | No | [linux] |
unprotected branch, job tags [linux] |
Yes if scope/status/capacity match. |
| R2 | Yes | [deploy] |
unprotected branch, job tags [deploy] |
No — protected policy rejects the ref. |
| R2 | Yes | [deploy] |
protected release branch, job tags [deploy]
|
Potentially yes; still verify user/ref/project policy. |
| R3 | No | [linux,gpu] |
job tags [linux] |
Yes — extra runner tags are allowed. |
| R3 | No | [linux,gpu] |
job tags [linux,gpu,cuda12] |
No — runner lacks one required tag. |
This satisfies the protected-runner reasoning goal without changing a valuable protected runner.
11. Credential-free evidence packet
Produce a short record containing:
- GitLab offering/version line used for the lab.
- Project path and synthetic branch name.
- Baseline runner IDs/types/status/tags only.
- Original checkpoint commit SHA and pipeline/job IDs.
- Prediction table.
- Screenshot/text of the pending job's requested tag and a separate runner inventory showing no match.
- Repair commit SHA and new pipeline result.
- If optional runner used: runner ID/description/tag/executor class and cleanup proof—never authentication token/config file.
12. Cleanup and rollback
After evidence capture:
# Restore the repository state you want to keep, then remove the synthetic branch.
git switch main
git push origin --delete ch14/checkpoint
git branch -D ch14/checkpoint
If you created the optional runner: pause it first, unregister the runner manager from the disposable host, delete the runner object from the project's Runners UI, then destroy the VM/container/config volume. Confirm it no longer appears in runner inventory. Cancel any intentionally pending synthetic job before deleting the branch.
--all-runners on a host that manages
anything except this disposable lab. Target the lab runner by its
unique name and verify the selected configuration first.
13. Final verification checklist
- The ZIP/course lab never required a paid tier or production runner.
- The expected eligibility matrix was written before execution.
- The pending job was explained by two independent observations.
- The original failure remains preserved; repair used a new commit rather than history rewrite.
- No token, secret, full environment, runner config, private network data, or host credential appears in evidence.
- The optional runner, runner manager, host/config volume, and synthetic branch are all removed.
- Any remaining pipeline is tied to an intentional repository commit and has no external side effect.
14. What Chapter 14 adds to the operating model
The production model now has an explicit compute authorization layer. GitLab decides that a job exists; runner scope/tags/protection decide which execution fleet may accept it; the executor and host/network/cache design determine what that code can affect. This closes the loop from Chapter 13's variable authorization to actual code execution.
15. Bridge to Chapter 15
Chapter 15 moves from where jobs run to
how many jobs may run concurrently and in what dependency
order: DAG pipelines, needs, parallel jobs/matrices,
resource groups, and concurrency. Runner capacity and isolation are
prerequisites for reasoning correctly about pipeline throughput.
Knowledge check
Why was the pending job considered a successful checkpoint result?
Because it intentionally proved the eligibility model: the job requested a tag absent from every available runner, so the scheduler correctly kept it pending.
What two observations prove a tag mismatch?
The job YAML/details show the requested tag, and an independent project runner inventory shows that no eligible runner has that tag.
Why does the repair use a new commit instead of rewriting the failed commit?
It preserves the original failure as evidence and makes the causal change auditable.
What extra cleanup is required after unregistering a modern UI-created runner manager?
Delete the GitLab-side runner object as well, then destroy the disposable host/config volume and verify the runner is absent.
Why can an untagged hosted runner be used for the mandatory path but not assumed to exist?
GitLab.com commonly provides hosted instance runners, but compute entitlement/capacity and offering configuration can vary; the lab therefore includes a no-runner validation path.
What does Chapter 15 depend on from this chapter?
A correct model of runner capacity/eligibility/isolation; DAG and parallelism decisions are meaningless if jobs cannot be safely scheduled onto suitable runners.
Summary
The checkpoint proved runner governance without privileged production infrastructure: inventory first, predict eligibility, run or statically validate the safe path, preserve an intentional pending job, identify the exact failed predicate, repair only the synthetic selector, and clean up both repository and runner state. Runner selection is now an auditable part of the GitLab delivery model.
Official references
- GitLab Docs — Get started with GitLab Runner
- GitLab Docs — Manage runners and runner scope
- GitLab Docs — Configure runners, tags, and protected runners
- GitLab Docs — New runner creation/registration workflow
- GitLab Docs — Register runners
- GitLab Docs — Runner commands and unregister behavior
- GitLab Docs — Runner executors
- GitLab Docs — Docker executor
- GitLab Docs — Shell executor
- GitLab Docs — Kubernetes executor
- GitLab Docs — Docker Autoscaler executor
- GitLab Docs — Instance executor
- GitLab Docs — Self-managed runner security
- GitLab Docs — Runner fleet scaling
- GitLab Docs — Instance-group autoscaler
- GitLab Docs — GitLab-hosted runners
- GitLab Docs — Hosted runners on Linux for GitLab.com
- GitLab Docs — Hosted runners for GitLab Dedicated
- GitLab Docs — Runners API
- GitLab Docs — Token overview / runner authentication tokens
- GitLab Docs — CI/CD YAML tags
- GitLab Docs — Protected branches and CI/CD
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.