Compliance Pipelines, Pipeline Execution Policies, Governance, Audit Evidence, and Enterprise Controls: Configuration, Design Choices, and Tradeoffs
Choose central enforcement, reusable components, preventive or detective controls, and exception models using explicit trust and evidence tradeoffs.
Learning objectives
-
Choose between
inject_policyandoverride_project_cifrom ownership requirements. - Separate voluntary reuse from enforced configuration.
- Design preventive and detective controls with measurable rollout evidence.
- Model expiring waivers separately from raw policy scope.
- Plan a compliance-pipeline migration without double enforcement.
Design rule: Governance strength is not proportional to centralization. Centralize the invariant that must not be removable, expose its origin, and leave harmless project choices local whenever they cannot weaken the control.
1. Four decisions, four different ownership boundaries
Before adding a policy, classify the need. Is it a non-negotiable invariant, reusable implementation, observation, or exception? That answer determines the proper layer.
Central enforcement vs autonomy
Enforce only controls that projects must not remove; leave ordinary build/test choices with teams.
Mandatory jobs vs reusable components
A component standardizes reuse by agreement. A policy enforces behavior even when the project would not opt in.
Preventive vs detective
Preventive controls stop later work; detective controls measure and retain evidence while allowing the pipeline to continue.
Permanent exclusion vs expiring waiver
A permanent exclusion becomes an alternate policy. Prefer owner, approver, reason, expiry, and reconciliation.
2. Choose pipeline strategy from the ownership model
Current pipeline execution policy documentation centers new designs
on inject_policy and override_project_ci.
The older inject_ci strategy is deprecated.
| Strategy | Configuration ownership | Best fit | Evidence/risk |
|---|---|---|---|
inject_policy |
Project CI remains; policy CI is injected as isolated policy configuration. | Mandatory gates while preserving project-owned build/test/deploy. | Prove policy job origin, stage, collision handling, variable visibility, and policy ref. |
override_project_ci |
Policy-controlled configuration defines the resulting pipeline; project CI is included deliberately if desired. | Stronger organization control of the entire configuration surface. | Higher coupling and blast radius; prove exact included project SHA/config path. |
inject_ci |
Legacy injection behavior. | Migration archaeology only. | Deprecated; do not make it the new default. |
3. Central enforcement versus project autonomy
Suppose every service must produce a governance proof but teams may choose pytest, Maven, Gradle, or another test stack. Put only the governance proof in policy. Application teams keep their test jobs. This minimizes the policy blast radius, keeps feedback local, and makes separation of duties understandable.
Central policy still has operational cost: one bad change can affect every linked descendant. Roll out to a small test scope first, protect the policy project default branch, require review, and retain a last-known-good policy ref.
4. Versioned reuse is not the same as enforcement
| Question | Reusable include/component | Pipeline execution policy |
|---|---|---|
| Who chooses it? | Project/template author. | Policy owner and scope. |
| Can project remove it? | Usually yes by editing project YAML. | Not while the project remains in scope and the policy is active. |
| What to version? | Component/include ref or digest. | Policy project revision + referenced policy CI revision. |
| What proves use? | Compiled YAML and project source SHA. | Policy origin/scope plus effective job graph and pipeline evidence. |
| Rollback unit | Project reference change. | Reviewed policy revision/scope change with potentially wider blast radius. |
5. Preventive and detective controls are complementary
Use a blocking .pipeline-policy-pre job when violating
the invariant must prevent later work. Use a non-blocking detective
job when you are measuring adoption, learning false-positive rates,
or preparing a rollout.
policy:detect-duplicate-scan:
stage: .pipeline-policy-pre
allow_failure: true
script:
- ./detect-duplicate-scan.sh
artifacts:
when: always
paths: [governance-evidence/duplicate-scan.json]
policy:required-governance-proof:
stage: .pipeline-policy-pre
allow_failure: false
script:
- ./verify-governance-proof.sh
Do not call a detective control “enforced” merely because the job exists. The observable state includes whether its failure blocks later execution.
6. A waiver is governed data, not a forgotten exclusion
A project exclusion in policy_scope is technical scope.
It does not inherently encode the business lifecycle of a waiver.
Store that lifecycle separately and reconcile it.
{
"id": "EX-CH36-042",
"project_id": 456,
"control": "governance-proof",
"owner": "service-owner@example.invalid",
"approved_by": "governance@example.invalid",
"reason": "temporary migration window",
"created_at": "2026-09-13T00:00:00Z",
"expires_at": "2026-09-20T00:00:00Z",
"policy_version_requested_against": "ch36-policy-v1.0.0",
"ticket": "SIM-CH36-42"
}
An expiry timestamp nobody reconciles is documentation, not enforcement. A production process should alert or fail when a record is expired but the technical exclusion still exists.
7. Variable precedence and protection are part of the control design
Pipeline execution policies can control whether user-defined
variables influence policy jobs with
variables_override. An allowlist model is easier to
reason about than “allow everything except the few names we
remembered.”
variables_override:
allowed: false
exceptions:
- CH36_NON_SECRET_MODE
Secret rule: never store a credential value directly in the policy repository. Policy files are reviewable plaintext. Use an appropriate protected/external secret mechanism and prove only the minimum variable/provider path reaches the policy job.
8. Tier, offering, and trust prerequisites
| Capability | Current availability | Trust prerequisite | Mandatory free path |
|---|---|---|---|
| Pipeline execution policies | Ultimate; GitLab.com, Self-Managed, Dedicated | Authorized policy owners; linked policy project; readable referenced CI config or supported automatic access. | Local simulator. |
| Security policy projects | Ultimate | Owner or custom policy-link permission; review/protected branch recommended. | Local policy directory in Git. |
| Compliance frameworks | Premium/Ultimate; policy enforcement can require Ultimate | Top-level/group compliance governance. | Synthetic framework/scope metadata. |
| Project/group audit events API | Premium/Ultimate | Appropriate role/API authentication; protect exports. | Local evidence/audit JSON. |
| CI Lint API | Free/Premium/Ultimate | Project visibility/authentication as required. | Local config inspection or optional disposable project. |
| Instance-wide policy management groups | Ultimate on Self-Managed/Dedicated | Administrator-level governance and separation of duties. | Simulate central policy ownership only. |
9. Migrate old compliance pipelines without double enforcement
Compliance pipelines are deprecated. Before migration, capture a representative old pipeline: source SHA, effective jobs, variable behavior, reports/artifacts, framework association, and evidence. Create the replacement pipeline execution policy in a narrow test scope. Compare old and new results, including duplicate jobs and exception behavior. Only then remove the old mechanism.
Do not run both mechanisms broadly and call duplication “defense in depth.” GitLab explicitly warns that mixing them can cause duplicated jobs, failures, or missing checks.
10. Worked decision: one control across four teams
| Decision | Choice | Reason | Observable proof |
|---|---|---|---|
| Enforcement | inject_policy pre-job |
Only the compliance invariant must be centralized. | Policy ref + policy job in every in-scope pipeline. |
| Implementation | Central policy-ci.yml pinned by ref |
One reviewable implementation; origin remains visible. | Referenced project/file/ref + content digest. |
| Rollout | Detective for 48h, then blocking | Measure false positives and queue cost first. | Report counts, then reviewed policy change. |
| Legacy service | Two-week waiver with temporary scope exclusion | Exception has owner/reason/expiry instead of hidden bypass. | Exception record + scope diff + reconciliation. |
| Runners | Keep team runners unless the control requires a trusted pool | Avoid unnecessary central execution coupling. | Runner ID/tags/protection/executor in policy job evidence. |
11. Rollback restores a known-good policy state and preserves history
Rollback through a reviewed policy change that restores the last known-good policy/CI ref or disables/narrows the broken policy. Preserve failed pipelines and audit evidence. Deleting the bad history destroys the data needed to explain the incident.
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.