GitLab CI/CD
Expert-level guidance for GitLab CI/CD pipeline configuration, development, optimization, debugging, and best practices. Covers GitLab 9.0 through 18.x with version-specific annotations.
When to Use This Skill
- Configuring or modifying
.gitlab-ci.ymlfiles - Debugging pipeline failures or unexpected job behavior
- Optimizing pipeline performance and compute minutes
- Setting up CI/CD components and include templates
- Managing runner infrastructure and autoscaling
- Implementing deployment strategies and environment lifecycle
- Configuring secrets, security scanning, and compliance
- Implementing semantic-release automation
Do Not Use This Skill When
- Working with GitHub Actions or other non-GitLab CI systems
- Managing GitLab instance administration (not CI/CD pipeline config)
- Writing application code unrelated to CI/CD
Quick Reference — Top 20 Keywords
| Keyword | Purpose | Detail |
|---------|---------|--------|
| script | Commands to execute in a job | Required for every job unless trigger is used |
| rules | Conditional job inclusion | Replaces deprecated only/except. See rules-patterns |
| needs | DAG dependency declaration | Jobs run as soon as dependencies finish, ignoring stage order |
| variables | Define CI/CD variables | Scoped to job, pipeline, or global. See variable scopes |
| include | Import external YAML | Types: local, project, remote, template, component |
| extends | Inherit from hidden jobs | Up to 11 levels deep; merged with job config |
| cache | Persist files between jobs | Use cache:key:files for content-addressable caching |
| artifacts | Pass files between stages | expire_in, expose_as, access control. See artifacts |
| image | Docker image for job | Set globally via default:image or per-job |
| services | Sidecar containers | Common: docker:dind, database services for testing |
| stages | Ordered execution groups | Jobs in same stage run in parallel by default |
| workflow | Pipeline-level rules | Controls whether pipeline is created. See workflow-rules |
| trigger | Start downstream pipeline | Parent-child (same project) or multi-project |
| environment | Deployment target | Tiers, auto_stop_in, on_stop. See environments |
| default | Global job defaults | image, before_script, retry, interruptible |
| parallel | Fan-out job execution | parallel: N or parallel:matrix for combinations |
| retry | Auto-retry on failure | retry:when for selective retry. See retry |
| when | Job execution condition | on_success, on_failure, always, manual, delayed |
| resource_group | Concurrency control | Ensures only one job per group runs at a time |
| interruptible | Allow auto-cancel | Set true on non-deployment jobs for pipeline efficiency |
Quick Reference — Top 10 Predefined Variables
| Variable | Value | Available |
|----------|-------|-----------|
| CI_COMMIT_REF_NAME | Branch or tag name | Always |
| CI_COMMIT_SHA | Full commit SHA | Always |
| CI_PIPELINE_SOURCE | Trigger type (push, merge_request_event, schedule, api, trigger) | Always |
| CI_PROJECT_DIR | Workspace directory path | Job only |
| CI_JOB_TOKEN | Auto-generated token for API calls | Job only |
| CI_REGISTRY_IMAGE | Container registry path | Always |
| CI_MERGE_REQUEST_IID | MR internal ID | MR pipelines only |
| CI_MERGE_REQUEST_SOURCE_BRANCH_NAME | MR source branch | MR pipelines only |
| CI_ENVIRONMENT_NAME | Current environment name | Deployment jobs only |
| CI_COMMIT_BRANCH | Branch name (not set for tags) | Branch pipelines only |
For the complete 170+ variable reference, see predefined variables.
Quick-Start Example
# Minimal pipeline — see references/ for each topic
stages: [build, test, deploy] # → types-and-triggers.md
variables: # → variables/definition.md
NODE_VERSION: "20"
default:
image: node:${NODE_VERSION}
cache: # → jobs/caching.md
key:
files: [package-lock.json]
paths: [node_modules/]
build:
stage: build
script:
- npm ci
- npm run build
artifacts: # → jobs/artifacts.md
paths: [dist/]
expire_in: 1 week
test:
stage: test
script: npm test
coverage: '/Statements\s*:\s*(\d+\.?\d*)%/' # → jobs/testing.md
needs: [build] # → jobs/execution-flow.md
deploy:
stage: deploy
script: ./deploy.sh
environment: # → pipelines/environments.md
name: production
rules: # → yaml/rules-patterns.md
- if: $CI_COMMIT_BRANCH == "main"
when: manual
needs: [test]
Pipeline Configuration
GitLab supports multiple pipeline types with distinct triggering rules and variable availability.
| Pipeline Type | Trigger | Key Variable |
|---------------|---------|--------------|
| Branch pipeline | Push to branch | CI_COMMIT_BRANCH |
| Tag pipeline | Push tag | CI_COMMIT_TAG |
| MR pipeline | MR created/updated | CI_MERGE_REQUEST_IID |
| Merged results | MR with merged results enabled | CI_MERGE_REQUEST_EVENT_TYPE=merged_result |
| Merge train | MR added to merge train | CI_MERGE_REQUEST_EVENT_TYPE=merge_train |
| Scheduled | Cron schedule triggers | CI_PIPELINE_SOURCE=schedule |
| Parent-child | trigger:include | CI_PIPELINE_SOURCE=parent_pipeline |
| Multi-project | trigger:project | CI_PIPELINE_SOURCE=pipeline |
Key gotcha: CI_MERGE_REQUEST_* variables are only available when CI_PIPELINE_SOURCE=merge_request_event. Use workflow:rules to prevent duplicate branch+MR pipelines.
References:
- Pipeline Types & Triggers
- Merge Request Pipelines — detached, merged results, merge trains, fork handling
- Downstream Pipelines — parent-child, multi-project, dynamic child
- Monorepo Strategies —
rules:changes, per-service child pipelines - Pipeline Optimization — DAG, parallelism, compute minutes
- Scheduled Pipelines — cron syntax, schedule-only patterns
- Manual Gates —
when:manual, approvals, deploy freezes - Cross-Project Pipelines — multi-project triggers, artifact sharing
- Notifications & Badges — pipeline badges, email/Slack integration
YAML Authoring
Pipeline YAML supports composition patterns for DRY, maintainable configurations.
| Pattern | Mechanism | Scope |
|---------|-----------|-------|
| extends | Job inheritance (11-level deep merge) | Same file or included files |
| YAML anchors (&/*) | Same-file reference | Single file only |
| !reference | Cross-file array extraction | Included files (GitLab 13.0+) |
| include | External YAML import | local, project, remote, template, component |
Key gotcha: YAML anchors do NOT work across include boundaries — use extends or !reference instead.
References:
- Keyword Reference — full .gitlab-ci.yml keyword documentation
- Rules Patterns —
rules:if,rules:changes,rules:exists - YAML Composition — anchors, extends,
!reference, merge keys - Workflow Rules —
workflow:rules,auto_cancel, naming - YAML Optimization — DRY pipeline design patterns
Variables
Variables follow an 11-level precedence hierarchy — knowing the order prevents hours of debugging.
Precedence (highest → lowest):
- Pipeline execution policy variables
- Scan execution policy variables
- Pipeline variables (trigger, scheduled, manual, API)
- Project variables (UI/API)
- Group variables (inherited, closest subgroup wins)
- Instance variables
dotenvreport variables- Job-level YAML
variables: - Default (global) YAML
variables: - Deployment variables
- Predefined variables
Note:
trigger:forward:pipeline_variables: trueelevates parent YAML variables to pipeline variable precedence (level 3) in downstream pipelines. It is a mechanism, not a separate precedence level.
Key gotcha: Variables defined in rules:variables override job-level variables but only when that rule matches.
References:
- Predefined Variables — 170+ variables by category and version
- Variable Precedence — 11-level hierarchy with override examples
- Masking & Protection — masking rules, ≥8 chars, protected scoping
- Variable Scopes — expansion in rules vs script vs services
- Variable Definition — YAML, UI, API,
options,description
Jobs & Execution
| Feature | Keyword | Key Detail |
|---------|---------|------------|
| DAG execution | needs: | Up to 50 dependencies; ignores stage ordering |
| Parallel fan-out | parallel:matrix: | Generates N×M jobs from variable combinations |
| Concurrency lock | resource_group: | Only one job per group runs at a time |
| Test reporting | artifacts:reports:junit: | Parses JUnit XML into MR widgets |
| Coverage | coverage: '/regex/' | Extracted from job log for MR display |
| Container builds | services: [docker:dind] | Or use Kaniko/Buildah for rootless builds |
Key gotcha: cache is for speed (may miss); artifacts are for correctness (guaranteed). Don't use cache to pass build outputs between jobs — use artifacts.
References:
- Testing Strategies — JUnit, coverage, quality reports
- Caching —
cache:key:files, fallback_keys, distributed cache - Artifacts —
expire_in,expose_as, cross-pipeline sharing - Execution Flow —
needs:,dependencies, DAG patterns - Docker Builds — DinD, Kaniko, Buildah, multi-stage
- Git Strategies —
GIT_STRATEGY,GIT_DEPTH, submodules - Retry & Resilience —
retry:when,allow_failure, timeout
Runner Infrastructure
Runners execute jobs. Executor choice determines security model, performance, and isolation.
| Executor | Isolation | Best For | Autoscalable | |----------|-----------|----------|--------------| | Docker | Container | Most CI jobs | Yes (docker-autoscaler) | | Kubernetes | Pod | Cloud-native, multi-container | Yes (native) | | Shell | None | Simple scripts, host access | No | | Docker Autoscaler | VM + Container | Cost-optimized cloud fleets | Yes | | Instance | VM | Full isolation | Yes (fleeting) | | VirtualBox | VM | Legacy, local testing | Yes (legacy) |
Key gotcha: Docker-in-Docker (dind) requires privileged: true — significant security risk. Prefer Kaniko for container builds when possible.
References:
- Runner Architecture — manager model, executor types, compatibility
- Executors — Docker, K8s, shell config and comparison
- Autoscaling — docker-autoscaler, fleeting, scaling policies
- Runner Security — tokens,
allowed_images, hardening - Performance —
concurrent,check_interval, tuning - Fleet Management — config.toml, Prometheus metrics
CI/CD Components
Components are reusable pipeline building blocks published to the CI/CD Catalog (GitLab 17.0+).
include:
- component: $CI_SERVER_FQDN/my-org/my-component@1.2.0
inputs:
stage: test
scan_level: full
Key gotcha: Always pin component versions (@1.2.0 or @1 for major). Unpinned components are a supply-chain risk.
References:
- Component Authoring — project structure,
spec:inputs, Catalog - Catalog — discovery, version pinning, evaluation
- Inputs — types, validation, defaults
- Testing — self-referencing, integration tests
- Security — pinning audit,
include:integrity
Semantic Release
Automated versioning with @semantic-release/gitlab and the to-be-continuous component.
Token setup: Requires GITLAB_TOKEN (project access token with api scope) or GL_TOKEN. Must be masked and protected.
Key gotcha: Set GIT_DEPTH: 0 in the release job — shallow clones break commit analysis.
References:
- GitLab Integration — plugin lifecycle, TBC, tokens
- Configuration —
.releaserc, presets, hooks, GPG - Testing — dry-run, verification
Deployment & Environments
| Strategy | Pattern | Risk | Rollback | |----------|---------|------|----------| | Rolling | Replace instances incrementally | Medium | Redeploy previous version | | Blue-Green | Switch traffic between environments | Low | Switch back | | Canary | Route % of traffic to new version | Low | Reduce to 0% | | Feature Flags | Toggle features in-app | Lowest | Disable flag |
Key gotcha: Always set auto_stop_in on dynamic environments. Without it, review apps accumulate and leak resources.
References:
- Deployment Strategies — blue-green, canary, rolling, feature flags
- Dynamic Environments — review apps, auto_stop, cleanup
- Infrastructure as Code — Terraform/OpenTofu, state
- Release Automation — changelog, orchestration
Security
| Feature | Keywords/Config | GitLab Tier |
|---------|----------------|-------------|
| Secret variables | Settings > CI/CD > Variables (masked + protected) | Free |
| Vault integration | secrets:vault: | Premium |
| OIDC/JWT auth | id_tokens: | Free (15.7+) |
| SAST | include: SAST.gitlab-ci.yml | Ultimate |
| Dependency scanning | include: Dependency-Scanning.gitlab-ci.yml | Ultimate |
| Container scanning | include: Container-Scanning.gitlab-ci.yml | Ultimate |
| Compliance frameworks | Project settings | Ultimate |
Key gotcha: CI_JOB_TOKEN default scope allows access to all reachable projects. Restrict it via Settings > CI/CD > Token Access.
References:
- Secrets Management — Vault, GCP, Azure, AWS, OIDC
- Security Scanning — SAST, DAST, container, dependency
- Compliance — frameworks, audit trails, governance
- Pipeline Security — CI_JOB_TOKEN scoping, protected runners, fork safety
Troubleshooting
Debugging decision tree:
- YAML syntax error? → Use Pipeline Editor or CI Lint API
- Job not starting? → Check
rules:evaluation — View "merged YAML" in Pipeline Editor - Variable not expanding? → Check scopes and precedence
- Cache miss? → Verify
cache:keymatches — check runner cache backend - Artifact not found? → Check
expire_in,dependencies:, andneeds:artifacts: - Runner issues? → Check tags, runner availability, executor config
- Need verbose output? → Set
CI_DEBUG_TRACE=true(⚠️ exposes secrets)
References:
- Troubleshooting Guide — editor, lint, debug trace, common errors
- FAQ & Gotchas — top 25+ categorized gotchas with fixes
Output Formats
When producing specific types of analysis, use the corresponding output format spec:
| Task | Output Style | |------|-------------| | Analyzing MegaLinter results | megalinter-analysis | | Debugging pipeline failures | troubleshooting-report | | Reviewing test results | ci-tester-report | | Reviewing pipeline architecture | ci-architecture-review |
Version Compatibility
Key feature introductions across GitLab versions:
| Version | Feature | Impact |
|---------|---------|--------|
| 12.5 | workflow:rules | Pipeline-level conditional creation |
| 13.0 | !reference tag | Cross-file YAML references |
| 15.3 | rules:changes:compare_to | Baseline comparison for rules:changes (GA 16.0) |
| 13.9 | needs: cross-stage | DAG dependencies across stages |
| 14.2 | include:rules | Conditional file includes |
| 15.0 | include:integrity | SHA-pinned includes for supply chain security |
| 15.1 | docker-autoscaler executor | Modern runner autoscaling (replaces docker+machine) |
| 15.7 | id_tokens: | OIDC/JWT for external auth (Vault, cloud providers) |
| 16.0 | CI/CD Components beta | Reusable pipeline building blocks |
| 16.3 | workflow:name | Dynamic pipeline naming |
| 16.6 | needs:parallel:matrix | Fan-in from matrix jobs |
| 17.0 | CI/CD Catalog GA | Component discovery and versioning |
| 17.2 | GIT_CLONE_EXTRA_FLAGS | Custom git clone flags + native clone FF |
| 17.4 | include:inputs | Pass inputs to any included file |
| 18.0 | run: keyword | CI Steps (experimental) — replaces script: model |
Related Resources
- GitLab CI/CD Documentation
- .gitlab-ci.yml Reference
- Predefined Variables
- CI/CD Component Catalog
- GitLab Runner Documentation
Workspace Examples
Annotated analyses of real pipelines from this workspace:
- MAP Terragrunt Pipeline — component includes, MegaLinter, release automation
- Component Include Patterns — template structure,
spec:inputsusage