GitLab CI/CD Cheatsheet
Keywords
Variables
Commands
API
A quick reference for daily GitLab CI/CD work. Pipeline and job keywords, predefined variables, runner commands, and API endpoints — all in one place, organized for fast lookup.
Cheatsheet
Reference
GitLab CI/CD
How to Use This Cheatsheet
This cheatsheet is organized for fast lookup, not for learning. If you're new to GitLab CI/CD, start with the full guides (linked at the bottom) and come back here when you need to remember a specific keyword or command. Each section is a self-contained reference — you don't need to read them in order.
The keywords are grouped by scope: pipeline-level keywords control the entire pipeline, job-level keywords control individual jobs, and script keywords control how commands execute. The variables section lists the most commonly used predefined variables, with their meaning and typical use. The API section covers the endpoints you'll use most often for automation.
Keep this page bookmarked. The most common lookups are: the syntax for rules, the difference between needs and dependencies, the format of id_tokens for OIDC, and the predefined variables for pipeline source and merge request context. These are the things that are easy to forget between projects.
Pro Tip: Use Ctrl+F (Cmd+F on Mac) to search this page. The keywords are written exactly as they appear in .gitlab-ci.yml, so searching for "needs" or "rules" jumps straight to the reference.
Pipeline Keywords
stages, variables, default, workflow, include — control the pipeline structure.
Job Keywords
script, image, stage, rules, needs, artifacts, cache — control individual jobs.
Variables
CI_COMMIT_SHA, CI_PIPELINE_SOURCE, CI_MERGE_REQUEST_IID — predefined values.
Commands & API
gitlab-runner commands, CI Lint API, pipelines API — for automation.
Pipeline-Level Keywords
Pipeline-level keywords control the entire pipeline — its structure, its variables, and whether it runs at all. They're defined at the top level of .gitlab-ci.yml, outside of any job. Understanding these is essential because they determine the shape of everything that follows .
# stages: define the order of stages
stages:
- build
- test
- deploy
# variables: define global variables
variables:
NODE_VERSION: "18"
APP_NAME: "my-app"
# default: default settings for all jobs
default:
image: node:18
before_script:
- npm ci
retry:
max: 2
when:
- runner_system_failure
# workflow: control when pipelines run
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
- when: never
# include: import external configuration
include:
- local: '/ci/build.yml'
- project: 'templates/ci'
ref: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0'
file: '/templates/test.yml'
- template: Security/SAST.gitlab-ci.yml
- remote: 'https://example.com/ci/standard.yml'
# cache: global cache configuration
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
- .npm/
# services: global services (rarely global)
services:
- postgres:15
# image: global default image
image: node:18
# before_script / after_script: global scripts
before_script:
- echo "Starting job $CI_JOB_NAME"
after_script:
- echo "Job finished"
# default vs job-level precedence:
# job-level settings override default settings
# For arrays (before_script), job-level replaces default
Job-Level Keywords
Job-level keywords control individual jobs. Each job has a name (the YAML key), a stage, and a script. Everything else — image, cache, artifacts, rules, needs — is optional configuration that shapes how and when the job runs. These are the keywords you'll use most often .
# Basic job structure
job_name:
# stage: which stage the job belongs to
stage: build
# image: Docker image for the job
image: node:18
# services: additional containers
services:
- postgres:15
- redis:7
# script: commands to execute (REQUIRED)
script:
- npm ci
- npm run build
# before_script / after_script
before_script:
- echo "Setup"
after_script:
- echo "Cleanup" # Runs even on failure
# rules: when the job runs
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- when: never
# needs: dependencies for DAG
needs:
- job: build
artifacts: true
- job: optional_job
optional: true
# dependencies: which artifacts to download
dependencies:
- build
# artifacts: files to save
artifacts:
paths:
- dist/
- coverage/
exclude:
- dist/**/*.map
expire_in: 1 week
when: on_success
reports:
junit: test-results.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
# cache: job-specific cache
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull
# variables: job-specific variables
variables:
NODE_ENV: "test"
DEBUG: "true"
# environment: deployment target
environment:
name: production
url: https://example.com
on_stop: stop_production
# when: when to run the job
when: on_success # or on_failure, always, manual, delayed, never
# allow_failure: whether failure blocks the pipeline
allow_failure: false
# retry: retry on failure
retry:
max: 2
when:
- runner_system_failure
- stuck_or_timeout_failure
# timeout: max job duration
timeout: 1h
# interruptible: can be cancelled by new pipeline
interruptible: true
# resource_group: concurrency control
resource_group: production
# tags: which runners can run this job
tags:
- docker
- linux
# parallel: run multiple instances
parallel: 5
# parallel:matrix: run with different variables
parallel:
matrix:
- OS: [ubuntu, alpine]
NODE: [18, 20]
# id_tokens: OIDC tokens
id_tokens:
AWS_ID_TOKEN:
aud: https://gitlab.example.com
# secrets: external secrets
secrets:
DATABASE_PASSWORD:
vault: production/db/password@ops
# extends: inherit from another job
extends: .base_job
# inherit: control what's inherited
inherit:
default: [image, before_script]
variables: [NODE_VERSION]
Predefined Variables
GitLab provides dozens of predefined variables that are available in every job. These are the most commonly used ones, grouped by purpose. For the complete list, see the GitLab CI/CD variables reference .
# Pipeline Context
$CI_PIPELINE_ID # Unique pipeline ID
$CI_PIPELINE_IID # Project-level pipeline ID
$CI_PIPELINE_SOURCE # push, merge_request_event, schedule, web, api, trigger
$CI_PIPELINE_URL # URL to the pipeline
# Commit
$CI_COMMIT_SHA # Full commit SHA
$CI_COMMIT_SHORT_SHA # 8-character SHA
$CI_COMMIT_BRANCH # Branch name (not available in tag pipelines)
$CI_COMMIT_TAG # Tag name (only in tag pipelines)
$CI_COMMIT_REF_NAME # Branch or tag name
$CI_COMMIT_REF_SLUG # Slugified branch/tag name
$CI_COMMIT_MESSAGE # Commit message
$CI_COMMIT_TIMESTAMP # ISO 8601 timestamp
$CI_COMMIT_AUTHOR # Author name and email
# Merge Request (MR pipelines only)
$CI_MERGE_REQUEST_IID # MR number
$CI_MERGE_REQUEST_ID # MR global ID
$CI_MERGE_REQUEST_TITLE # MR title
$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME # Source branch
$CI_MERGE_REQUEST_TARGET_BRANCH_NAME # Target branch
$CI_MERGE_REQUEST_LABELS # MR labels
$CI_OPEN_MERGE_REQUESTS # Open MRs for current branch
# Job
$CI_JOB_ID # Unique job ID
$CI_JOB_NAME # Job name (from .gitlab-ci.yml)
$CI_JOB_STAGE # Job stage
$CI_JOB_STATUS # success, failed, canceled, running
$CI_JOB_TOKEN # Job token (for API access)
$CI_JOB_URL # URL to the job log
# Project
$CI_PROJECT_ID # Project ID
$CI_PROJECT_NAME # Project name
$CI_PROJECT_PATH # Project path (group/project)
$CI_PROJECT_NAMESPACE # Group/namespace
$CI_PROJECT_URL # Project URL
$CI_DEFAULT_BRANCH # Default branch name
# Registry
$CI_REGISTRY # Registry URL
$CI_REGISTRY_IMAGE # Image path in registry
$CI_REGISTRY_USER # Registry username
$CI_REGISTRY_PASSWORD # Registry password/token
$CI_REGISTRY_IMAGE_TAG # Image tag (usually commit ref slug)
# Environment
$CI_ENVIRONMENT_NAME # Environment name
$CI_ENVIRONMENT_URL # Environment URL
$CI_ENVIRONMENT_SLUG # Slugified environment name
# Server
$CI_SERVER_URL # GitLab instance URL
$CI_SERVER_HOST # GitLab host
$CI_SERVER_VERSION # GitLab version
$CI_API_V4_URL # API v4 base URL
# Runner
$CI_RUNNER_ID # Runner ID
$CI_RUNNER_DESCRIPTION # Runner description
$CI_RUNNER_TAGS # Runner tags
# User
$GITLAB_USER_ID # User ID
$GITLAB_USER_LOGIN # Username
$GITLAB_USER_EMAIL # User email
$GITLAB_USER_NAME # User display name
Common Syntax Patterns
These are the syntax patterns that are easy to forget and often needed. Each is a short, copy-pasteable snippet that solves a common problem.
# Run on main branch only
rules:
- if: $CI_COMMIT_BRANCH == "main"
# Run on merge requests only
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Run on tags only
rules:
- if: $CI_COMMIT_TAG
# Run on schedule only
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
# Run when specific files change
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
- "frontend/**/*"
- "package.json"
# Manual job
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
# Prevent duplicate pipelines
workflow:
rules:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# DAG with needs
needs:
- job: build
artifacts: true
# Optional needs (job may not exist)
needs:
- job: optional_build
optional: true
# Cache with lockfile key
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
- .npm/
policy: pull
# Artifacts with reports
artifacts:
when: always
reports:
junit: test-results.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
# Environment with auto-stop
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_COMMIT_REF_SLUG.example.com
on_stop: stop_review
auto_stop_in: 1 week
# OIDC token for AWS
id_tokens:
AWS_ID_TOKEN:
aud: https://gitlab.example.com
variables:
AWS_ROLE_ARN: "arn:aws:iam::123456789012:role/gitlab"
AWS_WEB_IDENTITY_TOKEN_FILE: "/tmp/token"
before_script:
- echo "$AWS_ID_TOKEN" > "$AWS_WEB_IDENTITY_TOKEN_FILE"
# Deploy with Helm
deploy:
image: alpine/helm:latest
script:
- helm upgrade --install my-app ./chart
--namespace production
--set image.tag=$CI_COMMIT_SHORT_SHA
--wait --atomic
# Resource group for deployment
deploy:
resource_group: production
environment:
name: production
# Parallel test sharding
test:
parallel: 5
script:
- npm test -- --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL
# Matrix build
test:
parallel:
matrix:
- NODE_VERSION: ["18", "20"]
OS: ["ubuntu", "alpine"]
image: node:$NODE_VERSION
Runner Commands
These are the commands used most often when managing a GitLab Runner. They're for administrators who install, register, and troubleshoot runners, not for pipeline authors .
# Install runner (Linux)
curl -L --output /usr/local/bin/gitlab-runner \
"https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64"
chmod +x /usr/local/bin/gitlab-runner
# Register runner
sudo gitlab-runner register \
--non-interactive \
--url "https://gitlab.example.com/" \
--token "$RUNNER_TOKEN" \
--executor "docker" \
--docker-image "alpine:latest" \
--description "docker-runner" \
--tag-list "docker,linux"
# Start / stop / restart
sudo gitlab-runner start
sudo gitlab-runner stop
sudo gitlab-runner restart
# Check status
sudo gitlab-runner status
# Verify configuration
sudo gitlab-runner verify
# List all runners
sudo gitlab-runner list
# Unregister runner
sudo gitlab-runner unregister --name "docker-runner"
# Run in debug mode (temporary)
sudo gitlab-runner --debug run
# View logs (systemd)
sudo journalctl -u gitlab-runner --no-pager | tail -50
# View logs (file)
tail -50 /var/log/gitlab-runner/gitlab-runner.log
# Config location
/etc/gitlab-runner/config.toml
# Edit config
sudo vim /etc/gitlab-runner/config.toml
# Reload config (no restart)
sudo gitlab-runner restart
# Common config.toml settings
concurrent = 4 # Global concurrency
[[runners]]
name = "docker-runner"
url = "https://gitlab.example.com/"
token = "..."
executor = "docker"
limit = 2 # Per-runner concurrency
[runners.docker]
image = "alpine:latest"
privileged = false
volumes = ["/cache"]
memory = "2g"
dns = ["8.8.8.8"]
[runners.cache]
Type = "s3"
[runners.cache.s3]
BucketName = "my-cache"
BucketLocation = "us-east-1"
API Endpoints
These are the GitLab API endpoints used most often in CI/CD automation. All require authentication via a personal access token (PRIVATE-TOKEN header) or a job token (JOB-TOKEN header) .
# Base URL
# https://gitlab.example.com/api/v4
# Authentication
# --header "PRIVATE-TOKEN: "
# --header "JOB-TOKEN: $CI_JOB_TOKEN"
# Pipelines
GET /projects/:id/pipelines
GET /projects/:id/pipelines/:pipeline_id
POST /projects/:id/pipeline # Trigger pipeline
POST /projects/:id/pipelines/:id/retry
POST /projects/:id/pipelines/:id/cancel
# Jobs
GET /projects/:id/jobs
GET /projects/:id/jobs/:job_id
GET /projects/:id/jobs/:job_id/trace
POST /projects/:id/jobs/:job_id/retry
POST /projects/:id/jobs/:job_id/cancel
POST /projects/:id/jobs/:job_id/erase
# CI Lint
POST /projects/:id/ci/lint # Validate .gitlab-ci.yml
POST /projects/:id/ci/lint # Simulate pipeline creation
# Variables
GET /projects/:id/variables
POST /projects/:id/variables
PUT /projects/:id/variables/:key
DELETE /projects/:id/variables/:key
# Environments
GET /projects/:id/environments
POST /projects/:id/environments
PUT /projects/:id/environments/:id
DELETE /projects/:id/environments/:id
# Deployments
GET /projects/:id/deployments
POST /projects/:id/deployments/:deployment_id/rollback
# Feature flags
GET /projects/:id/feature_flags
POST /projects/:id/feature_flags
PUT /projects/:id/feature_flags/:name
DELETE /projects/:id/feature_flags/:name
# Runners
GET /projects/:id/runners
GET /runners/:id
POST /runners/:id/pause
POST /runners/:id/resume
# Registry
GET /projects/:id/registry/repositories
DELETE /projects/:id/registry/repositories/:repository_id
# Common examples
# Trigger a pipeline
curl --request POST \
--header "PRIVATE-TOKEN: $TOKEN" \
--url "https://gitlab.example.com/api/v4/projects/:id/pipeline" \
--data "ref=main" \
--data "variables[ENV]=production"
# Validate .gitlab-ci.yml
curl --request POST \
--header "PRIVATE-TOKEN: $TOKEN" \
--url "https://gitlab.example.com/api/v4/projects/:id/ci/lint" \
--data "content=$(cat .gitlab-ci.yml)"
# Cancel a pipeline
curl --request POST \
--header "PRIVATE-TOKEN: $TOKEN" \
--url "https://gitlab.example.com/api/v4/projects/:id/pipelines/:id/cancel"
# List variables
curl --header "PRIVATE-TOKEN: $TOKEN" \
"https://gitlab.example.com/api/v4/projects/:id/variables"
Quick Diagnostics
These are the first checks to run when something goes wrong. Each one takes seconds and eliminates a large class of problems. Start with the cheapest check and work down.
# Pipeline doesn't run
# - Validate .gitlab-ci.yml: CI Lint or Pipeline editor
# - Check workflow:rules
# - Check job rules
# - Check branch is the default or has an MR
# Job not running
# - Check rules (if, changes)
# - Check only/except (if legacy)
# - Check pipeline source (branch vs MR)
# - Check tags match a runner
# Job stuck "pending"
# - Check runner is online: Settings > CI/CD > Runners
# - Check tags match: job tags vs runner tags
# - Check runner is not paused
# - Check concurrency (limit)
# - Check shared runners enabled
# Job fails "command not found"
# - Check image has the tool
# - Check PATH
# - Check before_script installs the tool
# - Run locally: docker run -it --rm sh
# Job killed (exit 137)
# - OOM: reduce memory or increase limit
# - Check runner config: memory setting
# - Check Kubernetes: KUBERNETES_MEMORY_LIMIT
# Cache not hitting
# - Check cache key: should not be $CI_COMMIT_SHA
# - Use key: { files: [package-lock.json] }
# - Check policy: pull for test jobs
# - Clear cache: Build > Pipelines > Clear runner caches
# Registry authentication fails
# - Check CI_REGISTRY_USER / CI_REGISTRY_PASSWORD
# - docker login with those credentials
# - Check DNS: getent hosts $CI_REGISTRY
# DNS resolution fails
# - Check /etc/resolv.conf in job
# - Add dns in config.toml (Docker executor)
# - Add extra_hosts for internal IPs
# - Use FF_NETWORK_PER_BUILD for services
# Include not found
# - Check path: starts with /
# - Check ref exists
# - Check file exists in that ref
# - Check access to included project
# Duplicate pipelines
# - Add workflow rule:
# if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
# when: never
# Debug commands
script:
- env | sort # List all variables
- cat /etc/resolv.conf # DNS config
- getent hosts gitlab.example.com # DNS resolution
- pwd && ls -la # Current dir
- node --version # Tool version
- echo "Branch: $CI_COMMIT_BRANCH" # Pipeline context
Frequently Asked Questions
What's the difference between needs and dependencies?
needs creates a DAG (execution order). dependencies controls which artifacts a job downloads. They're independent: you can have needs without dependencies (and vice versa), but usually you use them together.
What's the difference between rules and only/except?
rules is the modern, more flexible approach. only/except is the legacy syntax (deprecated in GitLab 18). Use rules for new pipelines and migrate existing ones when convenient.
How do I use environment-specific variables?
In Settings > CI/CD > Variables, set the "Environment scope" (e.g., production, staging). The variable is then only available to jobs with that environment. The same key can have different values per environment.
How do I trigger a pipeline from another pipeline?
Use trigger. For a child pipeline: trigger: { include: 'path/to/child.yml' }. For a multi-project pipeline: trigger: { project: 'group/project', branch: 'main' }. Use strategy: depend to wait for completion.
What are the CI/CD variable precedence rules?
From highest to lowest: pipeline variables (UI/API trigger) > project variables > group variables > instance variables > .gitlab-ci.yml job variables > .gitlab-ci.yml global variables > predefined variables.
How do I cache correctly?
Key by lockfile hash: key: { files: [package-lock.json] }. Cache dependencies (node_modules, .m2), not build outputs. Use pull-push on build jobs, pull on test jobs.
How do I use OIDC for cloud credentials?
Use id_tokens with the cloud's audience. For AWS, write the token to a file and set AWS_WEB_IDENTITY_TOKEN_FILE. For GCP, use Workload Identity Federation. For Azure, use federated credentials on an app registration.
Where can I find the complete reference?
The GitLab CI/CD reference (docs.gitlab.com/ee/ci/yaml/) is the authoritative source. This cheatsheet covers the most common patterns. For anything not covered here, check the reference.