Chapter 01Lesson 03~105 minutes

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.

ArchitectureTradeoffsDAG designLeast privilegeTrust 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

Design boundary — evidence crosses planes explicitly
            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.

Chapter 01 design rule: do not add DAG complexity until you can name the blocked critical-path dependency that it removes. Measure queue and execution time before optimizing.
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.

Do not choose a PAT because it “works everywhere.” Broad long-lived credentials increase blast radius and couple automation to a human identity. This chapter uses no credentialed API calls at all.

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.

Feature availability is not a security argument. If a paid approval mechanism is unavailable, simulate the state transition for learning; do not replace it with an unsafe production shortcut.

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:

  1. Use GitLab-native pipeline creation because the source/MR identity is central.
  2. Start with stages such as test and package; introduce DAG edges only if measured critical-path delay justifies them.
  3. Make unit tests blocking for integration; keep a new experimental quality check advisory until its signal is trusted.
  4. Produce an immutable package/image once and promote that exact output later rather than rebuilding at deployment time.
  5. Use ordinary isolated test runners for untrusted code and a separate trusted path for future deployment authority.
  6. 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.

Next lesson

Diagnostics, Failure Modes, Security, and Performance

Apply the state model to intentionally broken pipelines: valid YAML that creates the wrong graph, pending jobs with no eligible runner, SHA confusion, missing artifacts, unsafe troubleshooting, and green deployment jobs whose targets are still unhealthy.

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?

A GitLab job submits a long-running operation to an external platform. What must the pipeline preserve besides the successful HTTP response?

Why should untrusted test jobs and privileged production deployment jobs often use different runner trust paths?

Does a CI_JOB_TOKEN automatically authorize access to every private GitLab project?

A security feature requires a higher GitLab tier. What should a free-compatible course do?

Official references and version notes

Version and compatibility note

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.

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