GitLab CI/CD Foundations: .gitlab-ci.yml, Pipelines, Jobs, Stages, and Execution Model: Concepts, Architecture, and Mental Model
Build the GitLab CI/CD execution model from versioned configuration through pipeline creation, jobs, stages, runner selection, checkout, logs, artifacts, predefined variables, and job identity.
Learning objectives
- Explain the separation between GitLab pipeline creation (control plane) and runner job execution (execution plane).
- Relate .gitlab-ci.yml, pipeline source, pipeline, stage, job, runner, executor, checkout, log, artifact, and status without conflating them.
- Distinguish pre-pipeline, pipeline, and job-only variables and explain why evaluation timing matters.
- Explain how CI_JOB_TOKEN represents temporary job identity and why authentication does not imply unlimited authorization.
- Inspect pipeline, job, commit, runner, and token-related metadata before changing CI configuration.
.gitlab-ci.yml, Pipeline Editor/CI Lint,
jobs, stages, ordinary branch and merge-request pipelines, predefined
variables, artifacts, runner scheduling, and
CI_JOB_TOKEN are available on GitLab Free across
GitLab.com, Self-Managed, and Dedicated. GitLab-hosted runners are
provided for GitLab.com and GitLab Dedicated; Self-Managed
installations provide and operate their own runner capacity. Hosted
compute quotas, credits, machine types, and billing can change, so
this chapter never hard-codes them and provides a validation-only path
whenever a runner is unavailable. Current glab uses the
glab ci command family; older pipe/pipeline
aliases are deprecated.
1. Why a green pipeline can still be misunderstood
Chapters 01–09 built the hosted collaboration path from project boundaries through merge governance and integration. CI/CD adds a second operating system around that work. A commit does not “run itself.” GitLab reads versioned configuration, decides whether a pipeline exists, materializes jobs, schedules eligible jobs, and hands each job to a runner. The runner then prepares an execution environment, checks out source, injects job-scoped context, executes commands, uploads requested evidence, and reports status.
If those phases are blurred together, teams make dangerous assumptions: “the YAML is valid, so the pipeline must exist,” “the pipeline exists, so a runner must execute it,” or “the job has a token, so it can call any API.” This chapter replaces those assumptions with a state model you can inspect.
2. The core resource chain
flowchart TD A[Git commit / ref] --> B[.gitlab-ci.yml] B --> C[GitLab pipeline creation] C --> D[Pipeline] D --> E[Stage ordering] E --> F[Job queue] F --> G[Eligible runner] G --> H[Executor environment] H --> I[Checkout + variables] I --> J[script] J --> K[Job log / status] J --> L[Artifacts if configured]
The arrows are causal. GitLab reads configuration in the context of a commit/ref and pipeline source. It decides which jobs belong to the pipeline. Stage constraints make jobs eligible over time. A matching runner claims an eligible job, its executor creates the runtime context, and the resulting log/status/artifacts are reported back to GitLab.
| Object | Where it lives | What it means | Do not confuse it with |
|---|---|---|---|
| .gitlab-ci.yml | Repository commit | Versioned desired CI/CD configuration. | A running pipeline; configuration can exist without creating one. |
| Pipeline | GitLab hosted metadata | One evaluated execution graph for a source/ref/SHA. | A Git branch or runner process. |
| Stage | Pipeline scheduling model | A sequencing group; later stages normally wait for earlier stages. | A machine or deployment environment. |
| Job | Pipeline + runner lifecycle | One executable unit with its own status/log/context. | An entire pipeline. |
| Runner | Execution agent registered with GitLab | Requests and executes eligible jobs. | The server-side pipeline scheduler. |
| Executor | Runner implementation choice | How the runner creates the job environment, such as shell, Docker, or Kubernetes. | The runner identity itself. |
| Artifact | GitLab-managed job output | Files/reports deliberately uploaded after job execution. | The runner workspace or cache. |
3. Control plane versus execution plane
GitLab is the control plane. It parses/evaluates CI configuration, creates pipelines and jobs, tracks permissions and policy, queues work, and stores status/log/artifact metadata. GitLab Runner is the execution plane. A runner asks GitLab for work, receives a job it is eligible to execute, prepares an executor, obtains sources and variables, runs commands, and reports results.
This boundary matters for troubleshooting. A pipeline that was never
created is not a runner problem. A job that is
pending after pipeline creation may be a runner
eligibility/capacity problem. A command returning exit code 1 after
the job reaches running is an execution problem. Start
diagnosis at the phase that actually failed.
4. A pipeline has a source, ref, and commit identity
A pipeline is not simply “the pipeline for branch X.” GitLab records
why it was created. The predefined variable
CI_PIPELINE_SOURCE can distinguish sources such as
push, merge_request_event,
web, schedule, api,
trigger, parent_pipeline, and
pipeline. The source matters because
workflow:rules and job rules can produce
different graphs for different origins.
Always pair source with CI_COMMIT_SHA and the
ref-related variables. A branch name is movable. A SHA names the
commit the job is actually evaluating.
# Read-only from an authenticated local clone.
glab ci list --output json --per-page 10 --jq '.[] | {id,status,source,ref,sha,web_url}'
# For the current branch, inspect pipeline and job details.
glab ci get --output json --with-job-details --jq '{id,status,ref,sha,source,jobs}'
git rev-parse HEAD
git rev-parse origin/main
5. Configuration and variables exist in phases
GitLab predefined variables are not all available at the same time.
Pre-pipeline values exist before pipeline creation
and can influence which configuration is included.
Pipeline values exist while GitLab builds the
pipeline graph and can drive job rules.
Job-only values appear only after a runner starts a
job and therefore cannot decide whether that job or pipeline was
created.
| Phase | Examples | Can affect pipeline graph? | Operational question |
|---|---|---|---|
| Pre-pipeline |
CI_PIPELINE_SOURCE, selected project/ref
context
|
Yes, including early configuration choices. | Should GitLab even load/include this configuration? |
| Pipeline | Job/stage context and variables available while jobs are created | Yes, for supported job rules. | Which jobs are added to this pipeline? |
| Job-only |
CI_JOB_ID, CI_JOB_TOKEN,
CI_RUNNER_ID
|
No; the job already exists. | What identity/environment did the runner give this running job? |
6. Stages are the beginner scheduling model
With a stages-first pipeline, GitLab normally makes jobs in the same
stage eligible together and waits for the previous stage to succeed
before later stages proceed. If you omit stages, GitLab
has default stage names; if a job omits stage, it
belongs to test. Chapter 15 later introduces DAG
execution with needs; do not optimize away the simple
mental model before you can explain it.
stages:
- inspect
- verify
show_identity:
stage: inspect
script:
- printf 'source=%s\n' "$CI_PIPELINE_SOURCE"
- printf 'sha=%s\n' "$CI_COMMIT_SHA"
verify_checkout:
stage: verify
script:
- test "$CI_COMMIT_SHA" = "$(git rev-parse HEAD)"
7. Runner eligibility is a matching problem
Jobs enter a queue; runners request work. GitLab considers runner
scope, availability, protected status where applicable, and tags. If
a job declares tags, the runner must satisfy all of those
tags. A runner that allows untagged work can claim an untagged job.
A job that has no eligible runner stays pending;
changing the script cannot fix a scheduling mismatch.
GitLab.com projects can use GitLab-hosted runners when enabled and capacity/quota permits. Dedicated can use hosted runners configured for the tenant. Self-Managed operators provide runner infrastructure themselves. Never infer the executor or isolation level solely from a runner display name—inspect the actual runner/job metadata.
8. CI_JOB_TOKEN is temporary identity, not a universal secret
Just before a job runs, GitLab creates a CI_JOB_TOKEN.
It is job-only, valid while the job runs, and revoked when the job
finishes. Its effective access is related to the user who triggered
the pipeline but is deliberately limited to a documented subset of
GitLab resources. Cross-project access is additionally governed by
job-token allowlists and the triggering user still needs the
underlying permission.
CI_JOB_TOKEN.
GitLab masks the token in logs, but runner isolation and script
discipline remain essential. Presence can be verified without
revealing the value.
token_presence:
stage: inspect
script:
- |
if [ -n "${CI_JOB_TOKEN:-}" ]; then
echo "CI_JOB_TOKEN is present; value intentionally not printed"
else
echo "CI_JOB_TOKEN is unexpectedly absent"
exit 1
fi
9. Read-only inspection checklist before the first CI commit
-
Repository: does
.gitlab-ci.ymlalready exist on the target branch, and at what SHA? - Build → Pipelines: what sources, refs, SHAs, and statuses have recently existed?
- Build → Jobs: which jobs are pending/running/failed, and which runner is shown?
- Settings → CI/CD → Runners: which runners are available, and do they run untagged jobs? Do not expose registration/authentication tokens.
- Pipeline Editor / CI Lint: does GitLab accept the configuration, and does simulated creation produce the expected jobs?
- Compute: if hosted execution is unavailable, stop at validation and use the supplied expected-output fixture rather than purchasing capacity.
10. DevOps connection: prove what executed, where, and as whom
A production pipeline is evidence only when you can answer four questions: what configuration was evaluated, which commit/ref/source it represented, which runner/executor executed each job, and which identity/permissions the job received. Those questions connect source governance to later artifact, environment, release, registry, security, and deployment chapters.
Knowledge check
Why is a pipeline that never appears usually not a runner problem?
Runner scheduling happens after GitLab has created a pipeline and jobs. If creation is suppressed or configuration is invalid, there is no queued job for a runner to claim.
Why is CI_COMMIT_SHA stronger evidence than a branch name?
A branch ref can move after the pipeline starts; CI_COMMIT_SHA identifies the exact commit evaluated by that pipeline/job.
Can CI_JOB_TOKEN be used in workflow:rules to decide whether a pipeline exists?
No. CI_JOB_TOKEN is job-only and is created when the job is about to run, after pipeline creation.
A job declares tags [linux, docker]. Which runner is eligible?
A runner must satisfy all job tags, not merely one of them, and must also satisfy scope/protection/availability constraints.
Why is CI_JOB_TOKEN not equivalent to a personal access token?
It is short-lived, job-scoped, limited to documented resources, and authorization still depends on project/job-token policy and the triggering user’s permissions.
Summary
GitLab CI/CD is a state machine spanning versioned configuration, server-side pipeline creation, staged job eligibility, runner scheduling, executor preparation, source checkout, job-scoped identity, execution logs, artifacts, and final status. Correct operations begin by locating the failed phase and proving source/SHA/runner/identity rather than treating “the pipeline” as one opaque process.
Official references
- GitLab Docs — CI/CD
- GitLab Docs — Get started with GitLab CI/CD
- GitLab Docs — CI/CD YAML syntax reference
- GitLab Docs — Validate CI/CD configuration
- GitLab Docs — Pipeline editor
- GitLab Docs — Pipelines
- GitLab Docs — CI/CD jobs
- GitLab Docs — Job execution flow
- GitLab Docs — Predefined CI/CD variables
- GitLab Docs — CI/CD variables
- GitLab Docs — CI/CD job token
- GitLab Docs — Runners
- GitLab Docs — Configure runners
- GitLab Docs — GitLab-hosted runners
- GitLab Docs — Pipelines API
- GitLab Docs — Jobs API
- GitLab Docs — CI Lint API
- GitLab Docs — Job rules and CI_PIPELINE_SOURCE
- GitLab CLI — ci
- GitLab CLI — ci list
- GitLab CLI — ci get
- GitLab CLI — ci status
- GitLab CLI — runner list
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.