Continuous Integration and Delivery Foundations, GitLab Pipeline Architecture, and Delivery Flow: Configuration, Design Choices, and Tradeoffs
A good GitLab pipeline is not the one with the most YAML. It is the one whose control-plane choices, runner trust, evidence, and external side effects fit the delivery problem. This lesson turns the Chapter 01 execution model into design decisions: GitLab-native versus external orchestration, stages versus DAG flow, advisory versus merge-blocking checks, and least-privilege identity boundaries.
Learning objectives
- Choose between GitLab-native pipeline automation and external orchestration based on state ownership and evidence needs.
- Compare simple stage ordering with explicit DAG dependencies without prematurely optimizing a small pipeline.
- Distinguish advisory checks from controls that intentionally block integration or deployment.
- Separate repository/compiled configuration, runner infrastructure, GitLab-hosted evidence, and external target state.
- Apply least privilege and tier/offering awareness without making paid features mandatory for core learning.
1. Design from the state you need to control—not from available keywords
CI/CD design starts with a delivery problem. If the problem is “prove every commit passes unit tests,” the essential states are source identity, test configuration, job execution, and test evidence. If the problem is “promote one verified container to production,” the essential states expand to registry digest, deployment authorization, target identity, rollout status, and post-deploy health. The YAML syntax is downstream of those requirements.
A useful design question is: which system owns the truth for each state? GitLab owns pipeline, job, artifact metadata, environment/deployment records, and policy state. A runner owns temporary execution state while a job runs. Git owns source history. A registry owns image/package objects. A cloud or Kubernetes control plane owns deployment resources. Observability systems own target health. The pipeline should connect those systems without pretending that one green icon replaces all of them.
2. Separate four operational planes
flowchart TD
A[Repository + compiled CI configuration] --> B[GitLab pipeline / job control plane]
B --> C[Runner manager + executor]
C --> D[Temporary job workspace]
D --> E[GitLab artifacts / reports / registry records]
E --> F[Environment / deployment record]
F --> G[External cloud / cluster / service]
G --> H[Target health / operational evidence]
| Plane | Typical state | Design obligation |
|---|---|---|
| Source/configuration |
Commit, ref, .gitlab-ci.yml,
includes/components, rules.
|
Version and review the desired automation. |
| GitLab control plane | Pipeline/job graph, status, artifacts/reports, environment/deployment records, policy. | Preserve IDs and make decisions auditable. |
| Execution plane | Runner, executor, workspace/container/pod, local toolchain, network access. | Match trust, isolation, reproducibility, and capacity to workload. |
| External target | Registry, cloud resource, Kubernetes workload, application health. | Verify target identity and health independently. |
3. GitLab-native automation versus external orchestration
Use GitLab CI/CD when repository changes, merge-request context, project permissions, GitLab artifacts, deployment records, and developer feedback are central to the workflow. Keeping those decisions close to the repository usually improves traceability: the pipeline knows the source SHA, project identity, and job history without custom glue.
External orchestration can still be appropriate when a separate platform owns long-running workflows, fleet-wide reconciliation, cross-organization scheduling, or an operational process whose lifecycle is intentionally independent of one repository pipeline. In that case, GitLab should pass a narrowly scoped request with exact source/artifact identity and later reconcile the external result rather than assuming “request accepted” means “operation complete.”
| Question | GitLab-native bias | External-orchestrator bias |
|---|---|---|
| Primary trigger | Commit, MR, schedule, GitLab event. | Fleet/event stream or non-repository business process. |
| Primary evidence | Pipeline/jobs/reports/environments. | External workflow/resource state. |
| Duration | Bounded job/pipeline execution. | Potentially long-running reconciliation. |
| Authorization | Project/group roles, job identity, protected resources. | External identity and policy domain. |
| Failure recovery | Job retry/rerun plus explicit idempotency. | External engine compensation/reconciliation. |
Avoid two extremes: forcing every operational process into CI just because YAML is convenient, and moving simple repository CI into a second platform that obscures source/job evidence.
4. Stage flow versus DAG flow
Stages are a strong beginner default because they make the broad
order visible: for example,
build → test → package. Jobs in one stage can run
independently when runner capacity allows, and later stages normally
wait for earlier stages. This creates a simple mental model and
failure boundary.
GitLab's needs keyword creates explicit job
dependencies and can allow a job to begin before an unrelated job in
an earlier stage completes. That can shorten the critical path, but
it also requires you to reason about dependency edges, artifact
availability, optional jobs, and fan-in/fan-out. Chapter 09 owns
that topic in depth.
| Choice | Strength | Cost / risk | Good fit |
|---|---|---|---|
| Simple stages | Readable broad ordering. | Unrelated later work may wait. | Small pipelines and clear lifecycle phases. |
DAG with needs |
Expresses true dependencies and can reduce critical path. | Graph reasoning and artifact edges become more complex. | Measured bottlenecks in larger pipelines. |
5. Advisory checks versus blocking controls
Not every signal should have the same consequence. A new linter introduced to a legacy repository may begin as advisory while the team fixes historical debt. A unit test protecting a critical behavior may be merge-blocking from day one. A production deployment may require an authorization boundary that is separate from merge eligibility.
The design mistake is to equate “pipeline contains a job” with “this job governs the same decision.” Decide which state each check protects: developer feedback, merge eligibility, artifact promotion, environment deployment, or post-deploy health. Then make the enforcement point explicit and auditable.
| Signal | Possible policy | Evidence | Failure consequence |
|---|---|---|---|
| Formatting/lint | Advisory during rollout, later blocking. | Job/report tied to SHA. | Feedback or merge prevention. |
| Unit tests | Usually blocking for integration. | Job/test report. | Do not accept change. |
| Security finding | Policy depends on severity/context and tier features. | Scanner report + policy result. | Review, exception, or block. |
| Deployment approval | Separate authorization gate. | Approver/deployment record. | Do not mutate target. |
| Health check | Operational acceptance after deployment. | Target-side telemetry. | Rollback/incident response as designed. |
6. Choose identity by operation, not convenience
Pipelines often need an identity to call GitLab or an external
service. The safest design is to start from the operation and grant
the narrowest identity that can perform it. For GitLab-to-GitLab
operations supported by CI_JOB_TOKEN, the short-lived
job identity can be preferable to a broad personal access token.
Deployment-specific tokens, project/group tokens, or deploy tokens
have different scopes. For supported cloud or secret-provider
integrations, later chapters prefer OIDC workload identity over
long-lived cloud keys.
Authentication and authorization remain separate. Possessing a
CI_JOB_TOKEN does not mean the job can access every
project. Current GitLab documentation restricts its resource
surface, ties effective access to the triggering user's permissions,
and uses target-project allowlisting for many cross-project
scenarios.
7. Runner selection is a trust and reproducibility decision
Runner labels/tags and executors are not just scheduling conveniences. A job that only runs unit tests on untrusted merge-request code should not automatically share the same privileged execution path as a production deployment job. Separate runner pools, protection, executor privilege, network reachability, and ephemeral workers are tools for containing risk.
Reproducibility also matters. Record the Runner version and relevant execution image/toolchain identity. A pipeline result from “some runner with whatever happens to be installed” is harder to reproduce than a job whose runtime assumptions are explicit. Do not overclaim determinism, however: even pinned container images can reach mutable package repositories or network services unless those dependencies are controlled too.
8. GitLab-native integrations versus external tools
GitLab can ingest native report formats, store artifacts, manage registries, record deployments, and present security or quality results when configured. External tools may still be the right engines for testing, scanning, deployment, or observability. The design goal is not to avoid external tools; it is to preserve clear ownership and evidence.
For example, a test framework owns test execution semantics, while GitLab can ingest a JUnit report and associate it with the pipeline. A cloud provider owns resource health, while GitLab can record a deployment and surface a URL. A vulnerability scanner owns its detection logic, while GitLab can ingest findings. Do not transform “GitLab displays the result” into “GitLab generated or independently verified every result.”
9. Tier and offering are architectural constraints
GitLab capabilities can differ by Free/Premium/Ultimate tier, user role, project/group/instance scope, and offering (GitLab.com, Self-Managed, Dedicated). A good course and a good platform design label those dependencies instead of silently making them mandatory.
Chapter 01 uses only core CI/CD concepts and a free-compatible lab. When later chapters discuss protected environment approvals, advanced security, compliance, or enterprise policies, the lesson must identify the current tier/offering prerequisite and include a faithful simulation or architecture exercise when the feature is unavailable.
10. Worked design scenario: a small web service
Suppose a repository contains a small service with unit tests and a container build. The team wants fast feedback on every branch and a controlled deployment later. A reasonable early design is:
- Use GitLab-native pipeline creation because the source/MR identity is central.
-
Start with stages such as
testandpackage; introduce DAG edges only if measured critical-path delay justifies them. - Make unit tests blocking for integration; keep a new experimental quality check advisory until its signal is trusted.
- Produce an immutable package/image once and promote that exact output later rather than rebuilding at deployment time.
- Use ordinary isolated test runners for untrusted code and a separate trusted path for future deployment authority.
- Record source SHA, pipeline/job IDs, runtime identity, artifact digest, and—when deployment is added—target-side health.
This design is intentionally modest. It leaves room for components, child pipelines, environments, OIDC, policies, and autoscaling later without requiring those concepts to understand a two-job CI graph.
11. Decision matrix
| Decision | Prefer the simpler option when… | Escalate complexity when… | Evidence to preserve |
|---|---|---|---|
| Stages vs DAG | Pipeline is small and stage waits are acceptable. | Measured unrelated waits dominate critical path. | Job dependencies, queue/start/end times. |
| GitLab vs external orchestrator | Lifecycle is repository/pipeline-centric. | External platform owns long-lived reconciliation. | Request ID plus external final state. |
| Advisory vs blocking | Signal is new/noisy or informational. | Failure violates an explicit integration/release policy. | Job/report and policy decision. |
| Shared vs separated runner paths | Workloads share the same trust/privilege boundary. | Untrusted build and privileged deploy have different blast radius. | Runner scope/tags/protection/executor. |
| Long-lived secret vs workload identity | Only when short-lived/federated option is unsupported and risk is accepted. | OIDC or other short-lived identity is supported. | Identity audience/claims/policy, never secret values. |
12. Design exercise: state the boundary before writing YAML
For a hypothetical repository, write a one-page design note with four columns: state, owner, identity, and evidence. Include source SHA, pipeline graph, runner runtime, build artifact, deployment authorization, deployment target, and target health. Then mark each item as required now or deferred to a later chapter.
If you cannot name who owns a state or how you will prove it, adding more CI keywords will not solve the design gap.
Summary
Good GitLab CI/CD architecture is explicit about state ownership, trust, evidence, and side effects. Stages are a readable default; DAGs are a measured optimization. GitLab-native automation is strongest when repository context is central, while external orchestration needs explicit correlation. Checks should protect a named decision, identities should be least-privilege and short-lived where possible, and runner selection should reflect workload trust.
Knowledge check
A small pipeline has three clear sequential phases and no
measured latency problem. Should you immediately replace stages
with a dense needs DAG?
No. Use the simpler stage model until a measured dependency/critical-path problem justifies additional graph complexity.
A GitLab job submits a long-running operation to an external platform. What must the pipeline preserve besides the successful HTTP response?
Preserve a correlation/resource ID and reconcile the external final state. Request acceptance is not completion.
Why should untrusted test jobs and privileged production deployment jobs often use different runner trust paths?
They have different blast radius and authority. Separation reduces the chance that untrusted code can reach production credentials, networks, or privileged host capabilities.
Does a CI_JOB_TOKEN automatically authorize access
to every private GitLab project?
No. Its resource surface is narrower than a PAT, effective access depends on the triggering user's permissions, and cross-project access is governed by target authorization/allowlists.
A security feature requires a higher GitLab tier. What should a free-compatible course do?
Label the dependency precisely and provide a simulation or architecture exercise for the control. Do not remove the security concept or replace it with an unsafe shortcut.
Official references and version notes
- Get started with GitLab CI/CD — current first-principles overview of pipeline configuration, jobs, stages, and runners.
- CI/CD pipelines — pipeline sources, basic/stage/DAG pipeline forms, and current pipeline behavior.
- CI/CD YAML syntax reference — authoritative keyword semantics and compatibility notes.
- Predefined CI/CD variables — availability phases and runtime identity variables such as source, SHA, pipeline, job, and runner metadata.
- Runners — current runner scheduling and execution model.
- Specify when jobs run with rules — pipeline-source-aware job inclusion and duplicate-pipeline considerations.
- CI/CD job token — current job-token lifetime, resource surface, triggering-user relationship, and cross-project allowlist behavior.
- Security for self-managed runners — runner/executor threat boundaries used by the design discussion.
This lesson intentionally stays at the design boundary. Detailed
needs, merge controls, protected environments, OIDC,
components, and policy syntax appear in their dedicated chapters,
where their current tier/offering/version semantics must be
revalidated. Baseline statements here were checked against primary
GitLab documentation on 2026-09-11.
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.