Compliance Pipelines, Pipeline Execution Policies, Governance, Audit Evidence, and Enterprise Controls: Concepts, Architecture, and Mental Model
Build a beginner-first mental model for versioned GitLab CI/CD governance, policy injection, audit evidence, and exception lifecycle.
Learning objectives
- Explain why project CI configuration and organization policy are different control planes.
- Trace policy source and scope through compiled pipeline, enforced job, evidence, audit, and exception state.
- Distinguish pipeline execution policies from deprecated compliance pipelines and from reusable components.
- Inspect policy, pipeline, runner, and evidence state without changing production systems.
- State current tier/offering and variable/scope assumptions precisely.
Version and tier note: Documentation checked 2026-09-13. The mandatory learning path in this chapter is a free local simulation. Real GitLab pipeline execution policies are an Ultimate feature on GitLab.com, Self-Managed, and Dedicated. Legacy compliance pipelines are deprecated; design new enforcement around pipeline execution policies and verify the deprecation page before migration.
1. The problem: a project pipeline is not an organization policy
A normal .gitlab-ci.yml answers what one project wants
to run. Governance asks a different question:
what must run even when the project would prefer not to run it,
who is allowed to change that requirement, and what evidence
proves which policy version was in force?
Chapter 35 optimized latency and cost. Chapter 36 adds a control
plane above that optimized pipeline.
Without a visible policy origin, a green pipeline can be misleading. A project could remove a scan, run on an unapproved runner, override a policy variable, or depend on a waiver that never expires. The remedy is not “more YAML.” It is a chain of versioned policy, explicit scope, effective configuration, execution evidence, and reviewable exceptions.
2. Name the states before changing them
Policy source state
The security policy project,
.gitlab/security-policies/policy.yml, the
referenced CI file, its ref/commit, and the projects or
frameworks in scope.
Compiled pipeline state
The project configuration plus any policy-injected configuration after GitLab evaluates includes, policy strategy, conflicts, rules, and precedence.
Execution state
Pipeline/job IDs, job graph, runner/executor, image/tool identity, variables made available to policy jobs, and final job status.
Evidence state
Logs, reports, artifacts, API responses, configuration hashes, audit events, policy version, source SHA, and timestamps kept for review.
Exception state
A narrow waiver identity, owner, reason, affected control/project, creation record, expiry, approver, and the event that closes it.
External state
Deployments, registries, cloud/Kubernetes/IaC targets, or other systems a governed pipeline might mutate. A policy job succeeding does not prove external health.
3. Mental model: policy intent becomes pipeline evidence
Read the chain left to right. An organization or group chooses a control objective. Authorized maintainers encode it in a security policy project. The policy references a versioned CI configuration. During pipeline creation, GitLab determines whether the target project is in scope and combines or replaces configuration according to the selected strategy. Only then do jobs enter the project pipeline. An enforced job produces evidence; audit events record governance actions; any exception must have a separate lifecycle.
The arrows matter. A policy file in Git is not evidence that it
applied to a particular pipeline. A job named
policy:governance-proof is not evidence of policy
origin unless you can tie it to the policy project/version. An audit
event saying “policy changed” is not by itself the compiled
pipeline. The operating model needs all of those links.
flowchart TD
A[Organization or group control objective] --> B[Security policy project
policy.yml + version]
B --> C[Policy scope + strategy
include / exclude]
C --> D[Pipeline compilation
project config + enforced config]
D --> E[Project pipeline
policy and project jobs]
E --> F[Enforced gate + evidence]
F --> G[Audit event + evidence packet]
G --> H[Exception review
owner + reason + expiry]
H --> C
4. The vocabulary that prevents category mistakes
| Term | What it means here | What it does not mean |
|---|---|---|
| Security policy project | A special project whose policy file is linked to target groups/projects. | Not the application repository and not a secrets vault. |
| Pipeline execution policy | A policy that enforces CI/CD configuration/jobs across projects. | Not the deprecated compliance-pipeline mechanism. |
inject_policy
|
Add policy configuration while keeping policy and project YAML isolated. | Not a license for the project to override policy behavior. |
override_project_ci
|
Use policy-owned configuration as the controlling pipeline and explicitly include project CI when desired. | Not “merge everything automatically.” |
suffix
|
Defines collision behavior for job names: suffix on conflict
or fail with never.
|
Not a security boundary by itself. |
| Policy scope | Includes/excludes projects, groups, or compliance-framework labels. | Not a time-bounded waiver record. Scope exclusion needs governance metadata if used as an exception. |
| Audit event | A record of a governance-relevant action. | Not automatically a complete source→policy→pipeline evidence chain. |
| Compliance framework | A label/management construct that can participate in scope and compliance workflows. | Not itself an enforced pipeline job. |
5. Inspect first: prove the current state without mutation
On a real authorized GitLab environment, begin with read-only observations. The exact permissions and paid features vary, so the free path later reproduces the evidence model locally.
# Project source identity
printf 'project=%s\nref=%s\nsha=%s\nsource=%s\n' \
"$CI_PROJECT_PATH" "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA" "$CI_PIPELINE_SOURCE"
# Optional API observations: keep the token in an environment variable; never echo it.
export GITLAB_URL="https://gitlab.example.test"
export PROJECT_ID="123456"
# export GITLAB_TOKEN="..." # authorized, narrow, disposable only
curl --fail --silent --show-error \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$PROJECT_ID/pipelines?per_page=5" | jq .
# CI Lint is available on all tiers and can validate project-side YAML.
jq --null-input --arg yaml "$(cat .gitlab-ci.yml)" '{content:$yaml,dry_run:false,include_jobs:true}' \
| curl --fail --silent --show-error \
--header 'Content-Type: application/json' \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--data @- \
"$GITLAB_URL/api/v4/projects/$PROJECT_ID/ci/lint" | jq .
CI Lint proves the supplied project configuration and its includes in project context. Do not label that result “the effective enterprise policy” unless the inspected mechanism actually includes the applied policy. For a governed pipeline, also inspect the policy project/ref, policy page, actual pipeline/job graph, policy job trace, and audit evidence.
6. Current policy behavior that changes design decisions
| Current behavior | Why it matters operationally |
|---|---|
| Pipeline execution policies are Ultimate and support GitLab.com, Self-Managed, and Dedicated. | Mandatory learning must not depend on them; the chapter provides a faithful simulation. |
A security policy project stores policies in
.gitlab/security-policies/policy.yml and can be
linked to groups/projects.
|
Policy origin is versionable and reviewable. |
| Policy changes merged through an MR become effective after merge; direct/default-branch update behavior can have synchronization delay or different synchronization rules depending on current release. | Do not use “commit exists” as immediate proof of enforcement. Verify the current policy state. |
| A maximum of five pipeline execution policies per security policy project is documented by the current PEP schema. | Centralization needs deliberate composition rather than policy sprawl. |
inject_ci is deprecated;
inject_policy is the modern injection strategy.
|
Do not teach old examples as the default. |
Policy variables can have enforced precedence, and
variables_override controls exposure/override
behavior for user-defined variables.
|
Variable governance is part of policy design, not an afterthought. |
| Empty scope collections can be interpreted as no restriction rather than “nothing.” | A seemingly empty allowlist can accidentally broaden enforcement. |
| Users triggering a target pipeline need access to the referenced policy CI file unless the supported automatic-access setting is configured. | Access to policy source is an execution prerequisite that must be tested. |
7. Minimal evidence chain for one governed pipeline
That chain deliberately contains both configuration and execution. If a reviewer cannot answer “which policy revision affected pipeline 8421?” without guessing, the evidence model is incomplete.
8. Governance boundaries: what a policy does not magically prove
A policy can inject or control jobs, but it cannot make every runner trustworthy, turn a plaintext repository into a secret store, or prove a deployment target is healthy. Runner approval still depends on runner scope/tags/protection and executor isolation. Secrets still need protected variables or an external secret provider. Deployment authorization remains separate from build evidence. External systems still require independent verification.
Security boundary: GitLab documentation warns not
to store credentials as plaintext values in policy configuration.
Policy source is meant to be reviewable. Keep secrets out of
policy.yml and referenced CI YAML.
9. Where legacy compliance pipelines fit now
Compliance pipelines historically centralized project pipeline configuration through compliance frameworks. They are now deprecated. The current deprecation notice points users to pipeline execution policies and schedules removal in the GitLab 20.0 line; older migration text can still mention earlier removal targets. Treat the deprecation page for your deployed release as authoritative.
The migration lesson is broader than a product rename: inventory the old compliance framework, preserve an example pipeline and its evidence, create the new policy, compare effective jobs and variable behavior, then remove the old mechanism. Never enable both and assume they compose cleanly; GitLab explicitly warns that mixed enforcement can create duplicated, missing, or failing jobs.
10. Lesson summary
Enterprise CI/CD governance is a traceability problem as much as an enforcement problem. The object you govern is not just YAML; it is the relationship among policy source, scope, compiled configuration, pipeline, job/runner identity, evidence, audit record, and exception lifecycle. Lesson 2 turns that model into a disposable workflow.
Knowledge check
What must be recorded before claiming that a policy governed a project pipeline?
Record the policy project/version and scope, the project source SHA, the compiled/enforced configuration, pipeline and job IDs, and the resulting gate or audit evidence. Policy intent alone is not execution proof.
A project pipeline is green, but the expected enforced job is absent. Which layer should you inspect first?
Inspect policy scope and pipeline compilation/injection before debugging a runner. A runner cannot execute a job that policy/configuration compilation never created.
Why should an exception have an owner, reason, scope, and expiry?
Those fields bound the deviation, make it reviewable, and prevent a temporary bypass from silently becoming permanent governance state.
Version and compatibility note
GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.
Official references and version notes
Further reading — current official GitLab sources
Behavior and tier assumptions in this chapter were checked against the current documentation on 2026-09-13. Re-check these pages before applying the optional enterprise path because policy schemas and product tiers evolve.
- GitLab Docs — Pipeline execution policies
- GitLab Docs — Security policy projects
- GitLab Docs — Policy enforcement and separation of duties
- GitLab Docs — Compliance pipelines (deprecated)
- GitLab Docs — CI/CD variables and precedence
- GitLab Docs — CI Lint API
- GitLab Docs — Audit events
- GitLab Docs — Audit events API
- GitLab Docs — Compliance frameworks
- GitLab Docs — Deprecations and removals
Current assumptions used in this chapter: Version-sensitive YAML, Runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations must be verified against the exact GitLab, GitLab Runner, tool, and external-system versions used in production. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for reproducible work.
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.