GitLab CI/CD Cheatsheet

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]