Compliance & Governance
Compliance Frameworks
Audit Events
Policy Enforcement
Separation of Duties
Learn how to make your GitLab CI/CD pipelines audit-ready. From compliance frameworks and audit events to policy enforcement and separation of duties, build governance that satisfies auditors without slowing down developers.
Compliance
Audit
GitLab
Why Compliance and Governance Matter
Compliance is not optional for organizations in regulated industries. SOC 2, ISO 27001, HIPAA, PCI DSS, and GDPR all require demonstrable controls over who can change code, how changes are reviewed, and what evidence exists that those controls were followed. An auditor doesn't just want to hear that you have a deployment approval process — they want to see the audit trail proving it was followed for every production deployment over the past year.
GitLab provides compliance features that generate this evidence automatically. Compliance frameworks let you tag projects with the regulations they fall under, and then enforce a consistent set of controls across all of them. Audit events record every significant action — who merged what, who changed a setting, who deployed to production — in an immutable log. Compliance pipelines inject mandatory jobs into every project's pipeline, ensuring that required checks run even if the project's own .gitlab-ci.yml doesn't include them .
Governance is the broader practice: defining who has access to what, enforcing separation of duties (the person who writes code shouldn't be the one who approves it), and ensuring that policies are applied consistently across the organization. GitLab supports governance through protected branches, protected environments, merge request approvals, and security policies. The goal is not to slow down development but to make the safe path the easy path — the default workflow that developers follow is also the compliant one .
Core Principle: Compliance should be automated and invisible. Developers follow their normal workflow, and GitLab generates the evidence auditors need without manual effort.
Compliance Frameworks
Tags projects with regulations (SOC 2, ISO 27001). Enables consistent controls and reporting across all tagged projects.
Audit Events
Immutable log of all significant actions. Who did what, when, and from where. Available at project, group, and instance level.
Compliance Pipelines
Mandatory CI/CD jobs injected into every project's pipeline. Ensures required checks run regardless of project configuration.
Separation of Duties
Authors cannot approve their own merge requests. Enforced through approval rules and branch protection.
Security Policies
Scan execution and result policies enforced across projects. Blocks merges when critical vulnerabilities exist.
Compliance Reports
Pre-built reports showing which projects meet which controls. Evidence for auditors without manual collection.
Compliance Frameworks
A compliance framework is a label that identifies which regulatory standard a project must comply with. GitLab ships with built-in frameworks for common standards — SOC 2, ISO 27001, HIPAA, PCI DSS, GDPR — and allows organizations to create custom frameworks that match their internal policies. When a project is tagged with a framework, GitLab knows to apply the associated controls and to include the project in compliance reports for that framework .
The value of frameworks is consistency. Without them, each project team decides which controls to apply, and auditors must review each project individually. With frameworks, the organization defines the controls once, and every project tagged with that framework automatically inherits them. A SOC 2 audit becomes a matter of generating a report for all projects tagged with the SOC 2 framework, rather than manually inspecting each project's configuration.
Frameworks also enable compliance pipelines and compliance-specific approval rules. A project tagged with the PCI DSS framework can be required to run additional security scans, require approvals from a compliance officer, and prevent force-pushes to protected branches — all driven by the framework tag rather than by per-project configuration. This is essential for organizations with hundreds of projects and a small compliance team.
# Compliance frameworks are configured at the group level
# Group > Settings > Compliance > Frameworks
# Built-in frameworks:
# - SOC 2
# - ISO 27001
# - HIPAA
# - PCI DSS
# - GDPR
# - NIST 800-53
# - Custom frameworks
# Tag a project with a framework
# Project > Settings > General > Compliance frameworks
# Select one or more frameworks
# Framework controls (examples):
# - Require merge request approvals
# - Prevent force push to protected branches
# - Require compliance pipeline
# - Require security scan results
# - Restrict who can deploy to production
# Compliance frameworks API
# Create a custom framework
curl --request POST \
--header "PRIVATE-TOKEN: " \
--url "https://gitlab.example.com/api/v4/groups/:id/compliance_frameworks" \
--form "name=Custom Framework" \
--form "description=Internal security policy" \
--form "color=#FF0000"
# Assign framework to project
curl --request PUT \
--header "PRIVATE-TOKEN: " \
--url "https://gitlab.example.com/api/v4/projects/:id" \
--form "compliance_framework_ids[]=1"
# Compliance framework benefits:
# - Consistent controls across projects
# - Automated compliance reporting
# - Framework-specific approval rules
# - Framework-specific compliance pipelines
# - Audit-ready evidence
Compliance Framework Best Practices:
- Start with built-in frameworks (SOC 2, ISO 27001) and customize as needed
- Tag every project with the frameworks it must comply with
- Use compliance pipelines to enforce controls across all tagged projects
- Review framework assignments quarterly as projects evolve
- Document the controls each framework enforces for auditor clarity
Audit Events
Audit events are GitLab's immutable record of significant actions. Every time someone changes a project setting, merges a merge request, deploys to production, or modifies a CI/CD variable, GitLab records the action: who performed it, when, from what IP address, and what changed. These events cannot be edited or deleted, which is precisely what makes them valuable as audit evidence .
Audit events are available at three levels. Project-level events cover actions within a single project. Group-level events cover actions across all projects in a group, including group-level settings. Instance-level events (Ultimate only) cover all actions across the entire GitLab instance, including user management and system configuration. For most compliance purposes, group-level audit events provide the right balance of scope and manageability.
The events most relevant to CI/CD compliance include: merge request approvals, deployments to protected environments, changes to protected branches, modifications to CI/CD variables (especially protected ones), changes to project members and their roles, and changes to compliance framework assignments. These events form a chain of evidence: you can prove that a production deployment was approved by an authorized user, that the approval happened after the code review, and that no unauthorized user modified the pipeline configuration between approval and deployment .
# View audit events (UI)
# Project > Secure > Audit events
# Group > Secure > Audit events
# Admin > Monitoring > Audit events (instance-level)
# Audit events API
# List project audit events
curl --header "PRIVATE-TOKEN: " \
"https://gitlab.example.com/api/v4/projects/:id/audit_events"
# List group audit events
curl --header "PRIVATE-TOKEN: " \
"https://gitlab.example.com/api/v4/groups/:id/audit_events"
# List instance audit events (Ultimate)
curl --header "PRIVATE-TOKEN: " \
"https://gitlab.example.com/api/v4/audit_events"
# Filter by entity type
curl --header "PRIVATE-TOKEN: " \
"https://gitlab.example.com/api/v4/projects/:id/audit_events?entity_type=Deployment"
# Filter by date range
curl --header "PRIVATE-TOKEN: " \
"https://gitlab.example.com/api/v4/projects/:id/audit_events?created_after=2025-01-01&created_before=2025-12-31"
# Key audit events for CI/CD compliance:
# - merge_request.approved
# - merge_request.merged
# - deployment.created
# - deployment.succeeded
# - protected_branch.updated
# - ci_variable.created / updated / deleted
# - project_member.created / updated / deleted
# - compliance_framework.assigned
# Audit event structure (JSON)
# {
# "id": 12345,
# "author_id": 42,
# "author_name": "Jane Developer",
# "entity_id": 100,
# "entity_type": "Deployment",
# "details": {
# "custom_message": "Deployed to production",
# "target_id": 100,
# "target_type": "Deployment",
# "target_details": "v1.2.3 to production"
# },
# "created_at": "2025-01-15T10:30:00.000Z",
# "ip_address": "192.168.1.1"
# }
# Stream audit events to external systems (Ultimate)
# Group > Settings > Audit events > Streaming
# - Amazon S3
# - Google Cloud Storage
# - External HTTP endpoint
# - Webhook
Audit Event Retention:
- GitLab.com: audit events retained indefinitely
- Self-managed: retention depends on configuration (default: forever)
- Stream to S3 or external storage for long-term retention and analysis
- Audit events cannot be deleted by anyone, including administrators
Compliance Pipelines
A compliance pipeline is a CI/CD pipeline that GitLab injects into every project tagged with a compliance framework. Unlike a regular pipeline, which is defined entirely by the project's own .gitlab-ci.yml, a compliance pipeline is defined at the group level and cannot be overridden by the project. This ensures that mandatory checks — license scanning, security scanning, approval gates — run in every pipeline for every tagged project, regardless of what the project's maintainers configured.
Compliance pipelines are essential for organizations that need to enforce controls across many projects without manually auditing each one. A compliance pipeline might require that all projects run SAST and Dependency Scanning, that their results are reviewed before merge, and that production deployments include a compliance-specific approval step. The compliance team defines these requirements once in the compliance pipeline, and they apply automatically to every tagged project .
Compliance pipelines also interact with merge request approval rules. GitLab can be configured to require that the compliance pipeline succeeds before a merge request can be merged, even if the project's own pipeline passes. This creates a second layer of assurance: the project's tests ensure the code works, and the compliance pipeline ensures the code meets regulatory requirements. Both must pass for the merge to proceed.
# Compliance pipeline configuration
# Group > Settings > Compliance > Compliance pipelines
# Example compliance pipeline (.gitlab-ci.yml in compliance project)
stages:
- compliance
- test
- deploy
# Mandatory security scan
compliance_security_scan:
stage: compliance
image: registry.gitlab.com/security-products/sast:latest
script:
- /analyzer run
artifacts:
reports:
sast: gl-sast-report.json
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Mandatory license scan
compliance_license_scan:
stage: compliance
image: registry.gitlab.com/security-products/license-scanning:latest
script:
- /analyzer run
artifacts:
reports:
license_scanning: gl-license-scanning-report.json
# Mandatory compliance check (custom)
compliance_check:
stage: compliance
image: alpine:latest
script:
- |
echo "Running compliance checks..."
# Check for required files
if [ ! -f "LICENSE" ]; then
echo "ERROR: LICENSE file missing"
exit 1
fi
# Check for security policy
if [ ! -f "SECURITY.md" ]; then
echo "ERROR: SECURITY.md missing"
exit 1
fi
echo "All compliance checks passed"
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Project's own jobs still run
# (compliance pipeline is merged with project pipeline)
# Enable compliance pipeline for a group
# Group > Settings > Compliance > Compliance pipelines
# Select the project and ref containing the pipeline
# Apply to projects tagged with specific frameworks
# Result:
# - Every project in the group runs the compliance pipeline
# - Compliance jobs run before the project's own jobs
# - If compliance jobs fail, the MR cannot be merged
# - Project maintainers cannot disable compliance jobs
Compliance Pipeline Caveats:
- Cannot be bypassed: Project maintainers cannot disable compliance jobs
- Runs in addition to project pipeline: Both pipelines run; both must pass
- Adds pipeline time: Compliance jobs add overhead to every pipeline
- Requires Ultimate: Compliance pipelines are an Ultimate feature
Separation of Duties
Separation of duties is a core principle of internal control: no single person should have the ability to both make a change and approve it. In software development, this means the person who writes a merge request cannot be the one who approves and merges it. Without separation of duties, a developer with merge access could bypass review entirely, undermining the entire compliance framework. Auditors specifically look for evidence that this control is enforced.
GitLab enforces separation of duties through merge request approval rules. The most important setting is "Prevent approval by author" — when enabled, the author of a merge request cannot approve their own changes. Combined with "Prevent approvals by users who add commits" — which also blocks anyone who pushed code to the MR branch — this ensures that at least one person who did not write the code must review and approve it before merge .
For the highest-security environments, GitLab also supports "Prevent approval by commit author" (Ultimate), which blocks approval by anyone who authored a commit in the MR, even if they didn't push it directly. This closes the loophole where a developer could author a commit locally, have a colleague push it, and then approve it themselves. Combined with protected branches (which prevent direct pushes) and required approvals (which set a minimum number of approvers), this creates a robust separation of duties that satisfies even the strictest auditors.
# Merge request approval settings
# Project > Settings > Merge requests > Approvals
# Required approvals
# Set the minimum number of approvals required
# Typical: 1 for normal changes, 2 for production
# Prevent approval by author
# Author cannot approve their own MR
# Essential for separation of duties
# Prevent approvals by users who add commits
# Anyone who pushed to the branch cannot approve
# Prevents "I'll push a fix and approve it myself"
# Prevent approval by commit author (Ultimate)
# Anyone who authored any commit in the MR cannot approve
# Closes the local-commit-then-colleague-pushes loophole
# Approval rules (Premium/Ultimate)
# Define specific approval requirements
approvals:
- name: "Security Team"
approvals_required: 1
user_ids: [42, 43]
rule_type: regular
- name: "Compliance Officer"
approvals_required: 1
user_ids: [100]
rule_type: regular
# Configure via .gitlab-ci.yml (approval rules in MR)
# Or via UI: Settings > Merge requests > Approval rules
# Protected branches (prevent direct pushes)
# Settings > Repository > Protected branches
# - main: Maintainers can merge, No one can push directly
# - production: Maintainers can merge
# Protected environments (prevent unauthorized deploys)
# Settings > CI/CD > Protected environments
# - production: Maintainers can deploy
# - Required approvals: 1
# Example: production deployment requires approval
deploy_production:
stage: deploy
script:
- ./deploy.sh production
environment:
name: production
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual # Requires manual approval
# Combined controls for separation of duties:
# 1. Protected branches (no direct pushes)
# 2. Required approvals (minimum 1)
# 3. Prevent approval by author
# 4. Prevent approvals by commit adders
# 5. Protected environments (deploy approval)
# 6. Audit events (evidence of enforcement)
Separation of Duties Best Practices:
- Enable "Prevent approval by author" on all protected branches
- Enable "Prevent approvals by users who add commits"
- Require at least 1 approval, 2 for production changes
- Protect
main and release branches from direct pushes
- Use protected environments for production deployments
- Audit approval patterns quarterly to detect rubber-stamping
Security Policies
Security policies are a governance mechanism for enforcing security requirements across projects. A security policy defines rules — for example, "all projects must run SAST on every merge request" or "merges are blocked if critical vulnerabilities are found" — and GitLab enforces those rules automatically. Policies are defined at the group level and apply to all projects in the group, giving security teams central control without requiring per-project configuration .
GitLab supports two types of security policies: scan execution policies and merge request approval policies. Scan execution policies enforce that specific scans run on specific triggers (merge requests, schedules, pipelines). They can force a scan to run even if the project's .gitlab-ci.yml doesn't include it. Merge request approval policies require approvals based on scan results — for example, "require a security team approval if the MR introduces a critical vulnerability" — giving security teams a veto over risky changes.
Security policies are powerful because they're centralized and tamper-proof. A project maintainer cannot disable a group-level security policy, and any attempt to do so is recorded in audit events. This means the security team can be confident that every project is running the required scans and enforcing the required approvals, without manually auditing each project. For organizations with hundreds of projects and a small security team, this centralized enforcement is essential .
# Security policies are configured in the Security Policy project
# Group > Secure > Policies > New policy
# Scan execution policy (YAML)
# Enforce SAST on all merge requests
type: scan_execution_policy
name: Enforce SAST on MRs
description: Run SAST on every merge request
enabled: true
rules:
- type: pipeline
branches:
- "*"
cadence: "*/15 * * * *" # Or use branch-based trigger
actions:
- scan: sast
site_profile: ""
scanner_profile: ""
projects:
including:
- project_id: 100
- project_id: 101
# Merge request approval policy (YAML)
# Require approval if critical vulnerability found
type: approval_policy
name: Block critical vulnerabilities
description: Require security approval on critical findings
enabled: true
rules:
- type: scan_finding
scanners:
- sast
- dependency_scanning
- container_scanning
severities:
- critical
- high
vulnerabilities_allowed: 0
actions:
- type: require_approval
approvals_required: 1
user_approvers_ids:
- 42 # Security team lead
group_approvers_ids:
- 10 # Security team group
# Scan execution policy via UI:
# Group > Secure > Policies > New policy > Scan execution
# - Select scan type (SAST, DAST, etc.)
# - Set trigger (MR, schedule, pipeline)
# - Select projects to apply to
# Approval policy via UI:
# Group > Secure > Policies > New policy > Merge request approval
# - Set conditions (severity, scanner, etc.)
# - Set approvers (users or groups)
# - Select projects to apply to
# Security policy benefits:
# - Centralized enforcement
# - Cannot be disabled by project maintainers
# - All changes recorded in audit events
# - Consistent across all projects in group
# - Auditors can verify enforcement
Security Policies vs Compliance Pipelines:
- Security policies: Enforce specific security scans and approvals. Define at group level, apply to projects. Cannot be overridden.
- Compliance pipelines: Inject arbitrary CI/CD jobs into project pipelines. More flexible but requires more setup.
- Use both: Security policies for security-specific controls, compliance pipelines for broader regulatory requirements.
Complete Compliance Setup Example
Here's how a complete compliance setup might look for an organization with SOC 2 requirements. It combines frameworks, compliance pipelines, separation of duties, and audit events.
# 1. Compliance framework configuration (group level)
# Group > Settings > Compliance > Frameworks
# Create framework: "SOC 2"
# Built-in: SOC 2 (or customize)
# Apply to all projects handling customer data
# 2. Compliance pipeline (in compliance project)
stages:
- compliance
- test
- deploy
# Mandatory security scans
compliance_sast:
stage: compliance
image: registry.gitlab.com/security-products/sast:latest
script:
- /analyzer run
artifacts:
reports:
sast: gl-sast-report.json
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
compliance_dependency_scan:
stage: compliance
image: registry.gitlab.com/security-products/dependency-scanning:latest
script:
- /analyzer run
artifacts:
reports:
dependency_scanning: gl-dependency-scanning-report.json
compliance_license_check:
stage: compliance
image: alpine:latest
script:
- |
echo "Checking license compliance..."
if [ ! -f "LICENSE" ]; then
echo "ERROR: LICENSE file missing"
exit 1
fi
if [ ! -f "SECURITY.md" ]; then
echo "ERROR: SECURITY.md missing"
exit 1
fi
echo "License compliance passed"
# 3. Merge request approval settings (project level)
# Settings > Merge requests > Approvals
# - Required approvals: 2
# - Prevent approval by author: ✓
# - Prevent approvals by users who add commits: ✓
# - Prevent approval by commit author: ✓ (Ultimate)
# 4. Protected branches (project level)
# Settings > Repository > Protected branches
# - main: Maintainers can merge, No one can push
# - release/*: Maintainers can merge, No one can push
# 5. Protected environments (project level)
# Settings > CI/CD > Protected environments
# - production: Maintainers can deploy, Required approvals: 1
# 6. Compliance pipeline assignment (group level)
# Group > Settings > Compliance > Compliance pipelines
# - Project: compliance-project
# - Ref: main
# - Apply to: projects with SOC 2 framework
# 7. Audit event streaming (group level, Ultimate)
# Group > Settings > Audit events > Streaming
# - Destination: Amazon S3
# - Events: all
# - Retention: 7 years
# Result:
# - Every project with SOC 2 framework runs compliance pipeline
# - Every MR requires 2 approvals, author cannot approve
# - Direct pushes to main are blocked
# - Production deploys require approval
# - All actions are audit-logged and streamed to S3
# - Auditors can verify controls with generated reports
Troubleshooting Compliance & Governance
# Compliance framework not appearing
# - Check it's created at group level
# - Check you have Maintainer+ access
# - Check the framework is enabled
# Compliance pipeline not running
# - Check project is tagged with the framework
# - Check compliance pipeline project and ref are correct
# - Check compliance pipeline file exists
# - Check the project has no conflicting settings
# Merge request approval not enforced
# - Check "Prevent approval by author" is enabled
# - Check protected branches are configured
# - Check approval rules are set
# - Check the MR is targeting a protected branch
# Audit events missing
# - Check you have correct access level
# - Check the action was actually performed (not just attempted)
# - Check audit events are not filtered out
# - Check retention settings (if streaming)
# Separation of duties not working
# - Check approval rules are configured
# - Check "Prevent approval by author" is on
# - Check the author is not also an approver
# - Check the MR doesn't have auto-merge enabled
# Security policy not blocking merge
# - Check policy is enabled
# - Check project is in the policy's scope
# - Check scan results are available
# - Check policy conditions match the findings
# Audit event streaming not working
# - Check destination credentials
# - Check network connectivity
# - Check bucket/endpoint permissions
# - Check streaming is enabled
# Debug: list compliance frameworks
# GET /groups/:id/compliance_frameworks
# Debug: list audit events
# GET /projects/:id/audit_events
# GET /groups/:id/audit_events
# Debug: check project compliance status
# Project > Secure > Compliance
# Shows which frameworks are applied and their status
Frequently Asked Questions
What's the difference between a compliance framework and a compliance pipeline?
A framework is a label that tags projects with regulations (SOC 2, ISO 27001). A compliance pipeline is the actual CI/CD configuration that enforces controls for those projects. Frameworks determine which projects get which pipelines.
Can project maintainers disable compliance jobs?
No. Compliance jobs are defined at the group level and cannot be overridden by project configuration. Project maintainers cannot disable them, which is why they're trusted for audit evidence.
How long are audit events retained?
On GitLab.com, indefinitely. On self-managed, by configuration (default: forever). For long-term compliance, stream audit events to external storage (S3, GCS) with your own retention policy.
What's the difference between separation of duties and required approvals?
Required approvals set a minimum number of approvers. Separation of duties ensures that the approvers are different from the author. Both are needed for compliance.
Can I enforce different controls for different teams?
Yes. Use different compliance frameworks for different teams or projects. Each framework can have its own compliance pipeline, approval rules, and security policies.
What's the difference between security policies and compliance pipelines?
Security policies enforce security-specific controls (scans, approvals based on findings). Compliance pipelines can inject any CI/CD job for broader regulatory requirements. Use both: security policies for security, compliance pipelines for everything else.
Do compliance features require Ultimate?
Most do. Compliance frameworks, compliance pipelines, security policies, and audit event streaming are Ultimate features. Merge request approvals and audit events (basic) are available on Premium. Plan accordingly.
How do I prove compliance to an auditor?
Generate compliance reports from GitLab (compliance center for frameworks), export audit events, and show that controls (approvals, protected branches, security policies) are enabled. The audit trail proves they were followed.
Compliance is not a gate at the end of the pipeline — it's a property of the pipeline itself.