Skip to main content

GitLab CI Cheatsheet — 80+ Pipeline YAML Snippets, Rules, Artifacts, and API Reference

GitLab CI/CD cheat sheet — 80+ entries covering pipeline structure, job config, rules, variables, artifacts, runners, templates, security, and the GitLab API.

  • Runs locally
  • Category Developer & DevOps
  • Best for Formatting, validating, shrinking, or inspecting code-adjacent text.
Section:
77 entries
Pipeline (8)
stages

Defines the ordered list of stages. Jobs in the same stage run in parallel; the next stage starts only after all jobs in the current stage succeed. Any job without a `stage` key defaults to the `test` stage.

Examples
stages:
  - build
  - test
  - deploy
workflow:rules

Controls whether a pipeline is created at all. Evaluated before any job. Use to prevent duplicate pipelines (e.g., both a branch push and an MR event creating separate pipelines for the same commit).

Gotcha: Without `workflow:rules`, GitLab creates both a branch pipeline and an MR pipeline for the same commit. This is the most common cause of duplicate pipeline runs.

Examples
workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
    - if: '$CI_COMMIT_TAG'
default

Sets default values for all jobs in the pipeline. Supports: `image`, `before_script`, `after_script`, `tags`, `retry`, `timeout`, `interruptible`, `artifacts`, `cache`. Job-level keys override these defaults.

Examples
default:
  image: node:20-alpine
  before_script:
    - npm ci --prefer-offline
  retry:
    max: 2
    when: runner_system_failure
  interruptible: true
include

Imports YAML from external files into the current pipeline. Types: `local` (same repo), `project` (another GitLab repo), `remote` (HTTP URL), `template` (GitLab-provided templates). Files are merged, with later files overriding earlier ones.

Examples
include:
  - local: '.gitlab/ci/build.yml'
  - template: 'Security/SAST.gitlab-ci.yml'
  - project: 'my-group/ci-templates'
    ref: main
    file: '/templates/docker-build.yml'
trigger (multi-project pipeline)

Triggers a pipeline in another project. The downstream pipeline runs asynchronously unless `strategy: depend` is set, which causes the trigger job to mirror the downstream pipeline status.

Gotcha: Without `strategy: depend`, the trigger job always shows "passed" regardless of what happens in the downstream pipeline — broken deployments can go undetected.

Examples
deploy-downstream:
  trigger:
    project: my-group/deploy-repo
    branch: main
    strategy: depend
  variables:
    IMAGE_TAG: $CI_COMMIT_SHA
trigger (child pipeline)

Triggers a child pipeline from a generated or static YAML file within the same project. Child pipelines appear nested under the parent. Used for dynamic pipeline generation (`generate-config` job writes YAML, trigger reads it).

Examples
generate:
  script: python generate_pipeline.py > pipeline.yml
  artifacts:
    paths: [pipeline.yml]

run-child:
  trigger:
    include:
      - artifact: pipeline.yml
        job: generate
.gitlab-ci.yml skeleton

Minimal working pipeline with three stages. The `image` key selects the Docker image for all jobs by default. Each stage block can contain multiple jobs that run in parallel.

Examples
stages: [build, test, deploy]

image: alpine:3.19

build-job:
  stage: build
  script:
    - echo "Building…"

test-job:
  stage: test
  script:
    - echo "Testing…"

deploy-job:
  stage: deploy
  script:
    - echo "Deploying…"
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
include:component

References a CI/CD component from the GitLab catalog (GitLab 16.0+). Components are versioned, self-documented pipeline building blocks. Use `~latest` or pin a specific version tag for stability.

Examples
include:
  - component: gitlab.com/components/sast/sast@~latest
    inputs:
      stage: security
Jobs (9)
script / before_script / after_script

`script` is the only required key in a job — a list of shell commands. `before_script` runs before `script` (and overrides the global default). `after_script` always runs after `script`, even if `script` fails, so use it for teardown.

Examples
test-unit:
  stage: test
  before_script:
    - cp .env.test .env
  script:
    - npm test
  after_script:
    - rm -f .env
allow_failure

`allow_failure: true` lets the pipeline continue even if this job fails — the job shows an orange warning icon instead of red. Use for non-blocking quality checks. `allow_failure:exit_codes` lets you specify which exit codes are allowed.

Examples
lint:
  script: npm run lint
  allow_failure: true

# Allow only specific exit code
dependency-check:
  script: ./check-deps.sh
  allow_failure:
    exit_codes: [2, 3]
retry

Retries a failed job automatically. `max` sets the retry count (1 or 2). `when` restricts retries to specific failure types: `runner_system_failure`, `runner_unsupported`, `stuck_or_timeout_failure`, `script_failure`, `api_failure`, `always`.

Examples
deploy:
  script: ./deploy.sh
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure
timeout

Sets the maximum duration for a job. Overrides the project-level and runner-level timeout. Uses Go duration format: `30m`, `1h 30m`, `2h`. The job is cancelled if it exceeds the timeout.

Examples
e2e-tests:
  script: npm run test:e2e
  timeout: 2h 30m

unit-tests:
  script: npm test
  timeout: 10 minutes
when

`when` controls when a job runs in relation to the previous stage's status. Values: `on_success` (default), `on_failure` (only if previous failed), `always` (regardless), `manual` (needs human click), `delayed` (after `start_in` delay), `never`.

Gotcha: `when: manual` blocks the pipeline at that stage unless `allow_failure: true` is also set. Without it, subsequent stages will not run until someone clicks the job.

Examples
notify-on-failure:
  script: ./notify.sh
  when: on_failure

deploy-to-prod:
  script: ./deploy-prod.sh
  when: manual
  allow_failure: false

canary-deploy:
  script: ./canary.sh
  when: delayed
  start_in: 30 minutes
environment

Associates a job with a GitLab environment (creates it if it does not exist). Enables deployment tracking, environment-scoped variables, and the Environments dashboard. Use `environment:url` to show a live link in the GitLab UI.

Examples
deploy-staging:
  script: ./deploy.sh staging
  environment:
    name: staging
    url: https://staging.example.com

deploy-review:
  script: ./deploy.sh review/$CI_COMMIT_REF_SLUG
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$CI_COMMIT_REF_SLUG.review.example.com
    on_stop: stop-review
coverage

Extracts a coverage percentage from the job's script output using a regex. The captured number is shown in the GitLab UI, MR widget, and pipeline badges. Pair with `artifacts:reports:coverage_report` for line-level coverage.

Examples
test:
  script: pytest --cov=myapp --cov-report=term-missing
  coverage: '/TOTAL.*s+(d+%)$/'

test-node:
  script: npm test -- --coverage
  coverage: '/Liness*:s*(d+.?d*)%/'
pages

Special job name that deploys to GitLab Pages. Must produce an `artifacts:paths: [public]` directory. Pages jobs must be in the `pages` or `pages:` namespace and run on the default branch.

Examples
pages:
  stage: deploy
  script:
    - npm run build
    - mv dist/ public/
  artifacts:
    paths:
      - public
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
inherit

Controls whether job-level `variables` and `default` values are inherited from the global scope. `inherit:variables: false` prevents global variables from leaking into a job. `inherit:default: false` skips global `default` keys.

Examples
deploy:
  script: ./deploy.sh
  inherit:
    default: false
    variables:
      - DEPLOY_TOKEN
      - TARGET_ENV
Rules (8)
rules:if

Conditional job inclusion using CI/CD variable expressions. Rules are evaluated top-to-bottom; the first matching rule wins. If no rule matches, the job is excluded by default. Use `when: never` as a catch-all to explicitly exclude.

Examples
deploy:
  script: ./deploy.sh
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: on_success
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      when: never
    - when: never
rules:changes

Runs a job only when specified files have changed relative to the comparison target. Ideal for monorepo pipelines where only affected services should build. Use with `compare_to` to specify the base branch.

Gotcha: In new branches with no comparison target, `rules:changes` always returns true. Add a fallback `if` clause or use `compare_to` to prevent unnecessary runs.

Examples
build-frontend:
  script: npm run build
  rules:
    - changes:
        paths:
          - frontend/**/*
          - package-lock.json
        compare_to: 'refs/heads/main'
rules:exists

Runs a job only when specified files exist in the repository. Useful for optional build steps (e.g., only run a Docker build if a `Dockerfile` exists). Accepts glob patterns.

Examples
docker-build:
  script: docker build .
  rules:
    - exists:
        - Dockerfile
    - exists:
        - 'services/*/Dockerfile'
needs

Specifies job dependencies for DAG (directed acyclic graph) pipelines. A job with `needs` starts as soon as all listed jobs finish, ignoring stage order. Dramatically reduces pipeline duration for large pipelines.

Examples
test-unit:
  stage: test
  needs: [build]

test-e2e:
  stage: test
  needs:
    - job: build
      artifacts: true
    - job: seed-db
      artifacts: false
dependencies

Limits which previous jobs' artifacts are downloaded. By default every job downloads all artifacts from previous stages. Set `dependencies: []` to download no artifacts, or list specific jobs to download only theirs.

Gotcha: When using `needs`, set `artifacts: true/false` on each need entry instead of using the top-level `dependencies` key — they conflict when both are present.

Examples
deploy:
  stage: deploy
  script: ./deploy.sh
  dependencies:
    - build-app

e2e-tests:
  stage: test
  script: npm run test:e2e
  dependencies: []   # don't download any artifacts
only / except (legacy)

**Deprecated in favor of `rules`.** `only:branches` / `only:refs` limits jobs to specific branches. `except` excludes. Does not support boolean expressions. Cannot handle MR pipeline deduplication cleanly.

Examples
# Legacy — prefer rules:
deploy:
  script: ./deploy.sh
  only:
    - main
    - tags
  except:
    - /^experiment/.*/
Predefined variables (common)

GitLab sets dozens of `CI_*` variables automatically. Most-used: `$CI_COMMIT_BRANCH` (current branch name), `$CI_PIPELINE_SOURCE` (push/merge_request_event/schedule/api/trigger), `$CI_COMMIT_TAG`, `$CI_DEFAULT_BRANCH`, `$CI_COMMIT_SHA`, `$CI_ENVIRONMENT_NAME`.

Examples
# Common rule conditions:
rules:
  - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  - if: '$CI_PIPELINE_SOURCE == "schedule"'
  - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
  - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
rules:variables

Sets job variables conditionally based on which rule matched. Allows a single job definition to behave differently across environments without duplicate jobs.

Examples
build:
  script: docker build -t $IMAGE_TAG .
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      variables:
        IMAGE_TAG: $CI_REGISTRY_IMAGE:latest
    - if: '$CI_COMMIT_TAG'
      variables:
        IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
    - variables:
        IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
Variables (8)
variables (global)

Declares variables available to all jobs. Can be overridden at the job level. Variables set here are visible in the UI pipeline view (avoid secrets). Use GitLab CI/CD Settings → Variables for secrets.

Examples
variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"
  NODE_ENV: production
  FF_USE_FASTZIP: "true"   # GitLab feature flag
variables (job-level)

Scoped to a single job. Overrides global variables with the same name. Can reference other variables with `$VAR` or `${VAR}` syntax. Supports `expand` flag to disable variable expansion for values that contain `$`.

Examples
test-postgres:
  image: postgres:15
  variables:
    POSTGRES_DB: test_db
    POSTGRES_USER: ci
    POSTGRES_PASSWORD: ""
    CONN_STR: "postgres://${POSTGRES_USER}@postgres/${POSTGRES_DB}"
$CI_REGISTRY / Docker image variables

`$CI_REGISTRY` is the GitLab Container Registry hostname. `$CI_REGISTRY_IMAGE` is the full image path for the current project. `$CI_REGISTRY_USER` and `$CI_REGISTRY_PASSWORD` are temporary credentials for the current pipeline.

Examples
build-image:
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
dotenv artifact (pass vars between jobs)

Writes `KEY=VALUE` pairs to a file and declares it as a `dotenv` report artifact. Downstream jobs that reference this job in `needs` automatically receive the variables as environment variables.

Examples
build:
  script:
    - VERSION=$(git describe --tags)
    - echo "VERSION=$VERSION" > build.env
    - echo "IMAGE=$CI_REGISTRY_IMAGE:$VERSION" >> build.env
  artifacts:
    reports:
      dotenv: build.env

deploy:
  script:
    - echo "Deploying $IMAGE version $VERSION"
  needs:
    - job: build
      artifacts: true
variables:description / value / options

Adds metadata for pipeline-level variables used in manually-triggered pipelines. `description` shows a tooltip, `value` sets the default, `options` provides a dropdown. Useful for parameterized release pipelines.

Examples
variables:
  ENVIRONMENT:
    value: "staging"
    options:
      - "staging"
      - "production"
    description: "Target environment for deployment"
  LOG_LEVEL:
    value: "info"
    description: "Application log level"
$CI_JOB_TOKEN

A short-lived token GitLab generates for each job. Used to authenticate against the GitLab API, Container Registry, Package Registry, and other projects that allow CI job token access. Expires when the job finishes.

Examples
download-package:
  script:
    - curl --header "JOB-TOKEN: $CI_JOB_TOKEN"
        "https://gitlab.example.com/api/v4/projects/123/packages/generic/myapp/1.0/app.tar.gz"
        -o app.tar.gz
Masked variables

Variables marked "Masked" in GitLab Settings → CI/CD → Variables are redacted from job logs. The value must be at least 8 characters, consist of printable ASCII, and contain no newlines. Use masking for tokens, passwords, and keys.

Gotcha: Masked variables are NOT encrypted at rest in GitLab CE/EE — they are just redacted from logs. For secrets at rest, use HashiCorp Vault or a secrets management integration.

Examples
# In .gitlab-ci.yml, just reference the variable name:
deploy:
  script:
    - curl -H "Authorization: Bearer $DEPLOY_TOKEN" https://api.example.com/deploy
  # DEPLOY_TOKEN is masked in GitLab Settings → CI/CD → Variables
$CI_COMMIT_REF_SLUG

URL-safe, lowercase version of the branch or tag name. Slashes are replaced with hyphens. Useful for dynamic environment names and Docker tags that must be DNS-compatible.

Examples
# Branch "feature/my-feature" → "feature-my-feature"
deploy-review:
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$CI_COMMIT_REF_SLUG.review.example.com
  script:
    - kubectl create namespace $CI_COMMIT_REF_SLUG || true
Artifacts & Cache (8)
artifacts:paths

Uploads matching files/directories to GitLab at job completion. Available for download in the UI and to downstream jobs. Use `artifacts:name` to set the archive filename, `artifacts:when` to upload on failure.

Examples
build:
  script: npm run build
  artifacts:
    name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
    paths:
      - dist/
      - coverage/
    when: always        # upload even if script fails
    expire_in: 1 week
artifacts:expire_in

Sets when artifacts are automatically deleted. Accepts: `never`, `30 seconds`, `5 minutes`, `1 hour`, `1 day`, `2 weeks`, `1 month`, `1 year`. If omitted, uses the project-level or instance-level default.

Examples
build:
  artifacts:
    paths: [dist/]
    expire_in: 30 days

test:
  artifacts:
    paths: [coverage/]
    expire_in: never    # keep test reports forever
artifacts:reports:junit

Uploads a JUnit XML file to GitLab. Test results appear in the MR Test Summary panel and in the pipeline Test tab. Failed tests are highlighted inline in the MR diff view.

Examples
test:
  script:
    - pytest --junitxml=report.xml
    - npm test -- --reporters=junit --outputFile=junit.xml
  artifacts:
    reports:
      junit:
        - report.xml
        - junit.xml
artifacts:reports:coverage_report

Uploads a Cobertura or clover XML coverage report. GitLab renders line-level coverage highlighting in MR diffs, showing which lines are covered by tests.

Examples
test:
  script: pytest --cov=myapp --cov-report=xml:coverage.xml
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
artifacts:reports:sast / dependency_scanning

Security report artifacts. SAST, dependency scanning, container scanning, and secret detection results appear in the MR Security tab and in the Security Dashboard. Produced automatically by the `Security/SAST.gitlab-ci.yml` template.

Examples
include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml

# To consume manually:
semgrep-sast:
  artifacts:
    reports:
      sast: gl-sast-report.json
cache

Stores directories between pipeline runs to speed up dependency installation. `key` determines cache isolation — a changed key creates a new cache entry. `policy: pull` (download only) or `push` (upload only) skips unnecessary operations.

Examples
test:
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - node_modules/
    policy: pull-push   # default: download, run, upload
cache:key:files

Creates a cache key from the hash of one or more files. The cache is automatically invalidated when the file changes (e.g., when `package-lock.json` is updated). Best practice for dependency caches.

Examples
build:
  cache:
    - key:
        files: [package-lock.json]
      paths: [node_modules/]
    - key:
        files: [Gemfile.lock]
      paths: [vendor/bundle/]
artifacts:exclude

Excludes matching files from the uploaded artifact archive while keeping the broader `paths` pattern. Useful for excluding test fixtures, sourcemaps, or debug binaries from release artifacts.

Examples
build:
  script: npm run build
  artifacts:
    paths: [dist/]
    exclude:
      - dist/**/*.map
      - dist/**/*.test.js
Runners (8)
tags

Selects which registered runner executes the job. A runner is assigned if it has ALL the listed tags. Group/project-specific runners can be tagged (e.g., `docker`, `kubernetes`, `linux`, `gpu`) for workload routing.

Examples
build:
  tags:
    - docker
    - linux

gpu-train:
  tags:
    - gpu
    - cuda-12
image

Specifies the Docker image for the job (when using the Docker executor). Can be any public or private registry image. Append `@sha256:...` for digest-pinned images in security-sensitive pipelines.

Examples
test:
  image: python:3.12-slim

build-go:
  image: golang:1.22-alpine

security-scan:
  image:
    name: aquasec/trivy:0.50.1
    entrypoint: [""]
services

Runs additional Docker containers alongside the job container. Services share a network namespace and are accessible by their image name as a hostname. Use for databases, message queues, and other test dependencies.

Examples
test-integration:
  image: node:20
  services:
    - name: postgres:15
      alias: db
    - name: redis:7-alpine
      alias: cache
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: ci
    DATABASE_URL: "postgres://ci@db/test"
parallel: N

Creates N identical concurrent job instances. Each instance gets `$CI_NODE_INDEX` (1-based) and `$CI_NODE_TOTAL` to partition work. Use to shard a large test suite across multiple runners.

Examples
rspec:
  script:
    - bundle exec rspec --format progress
        --only-failures-at-index $((CI_NODE_INDEX - 1))
        --shard-count $CI_NODE_TOTAL
  parallel: 5
parallel:matrix

Creates one job per combination of variable values. GitLab automatically generates unique job names for each combination. Matrix jobs run concurrently and are visible as separate rows in the pipeline graph.

Examples
build:
  script: make build TARGET=$TARGET ARCH=$ARCH
  parallel:
    matrix:
      - TARGET: [linux, darwin, windows]
        ARCH: [amd64, arm64]
      - TARGET: linux
        ARCH: [386]
runner executor types

Executors determine how the runner runs jobs. `shell`: directly on the host. `docker`: in a fresh container per job. `kubernetes`: in a Kubernetes pod. `docker-autoscaler` / `instance` (GitLab 16+): ephemeral VMs. `docker` with `privileged: true` enables Docker-in-Docker (DinD).

Examples
# In /etc/gitlab-runner/config.toml:
[[runners]]
  name = "docker-runner"
  executor = "docker"
  [runners.docker]
    image = "alpine:latest"
    privileged = true    # needed for DinD
DOCKER_HOST / dind service

Docker-in-Docker pattern for building Docker images inside a CI job. Uses the `docker:dind` service and sets `DOCKER_HOST` to connect to it. Alternative: use `kaniko` or `buildah` for rootless image building.

Gotcha: DinD requires the runner to be in `privileged` mode, which is a significant security risk on shared runners. Prefer `kaniko` for shared/untrusted environments.

Examples
build-image:
  image: docker:24
  services:
    - docker:24-dind
  variables:
    DOCKER_HOST: tcp://docker:2376
    DOCKER_TLS_CERTDIR: "/certs"
  script:
    - docker build -t myapp .
    - docker push myapp
artifacts:untracked

Uploads all files not tracked by git. Useful for capturing build outputs in interpreted or compiled language projects where the build tool generates files GitLab should archive.

Examples
build:
  script: go build ./...
  artifacts:
    untracked: true
    paths:
      - bin/
Templates (7)
extends

Inherits the configuration of another job (or hidden job starting with `.`). Deep-merges the parent and child YAML. Lists in child completely replace the parent list (no list-merge). Use hidden jobs (`.`) as abstract base templates.

Examples
.deploy-base:
  stage: deploy
  before_script:
    - apt-get install -y curl
  retry:
    max: 2

deploy-staging:
  extends: .deploy-base
  script: ./deploy.sh staging
  environment:
    name: staging

deploy-prod:
  extends: .deploy-base
  script: ./deploy.sh prod
  when: manual
YAML anchors (&, *, <<)

Native YAML anchors for reusing configuration within the same file. `&anchor-name` defines an anchor, `*anchor-name` references it, `<<: *anchor-name` merges it. Unlike `extends`, anchors cannot cross-file and only work within a single YAML document.

Examples
.node-setup: &node-setup
  image: node:20-alpine
  cache:
    key:
      files: [package-lock.json]
    paths: [node_modules/]

test:
  <<: *node-setup
  script: npm test

lint:
  <<: *node-setup
  script: npm run lint
!reference

GitLab-specific tag that references a nested key in another job or map. More powerful than YAML anchors because it can reach into nested structures. Used to compose complex `script` or `rules` blocks from multiple sources.

Examples
.setup-steps:
  script:
    - apt-get update -qq
    - apt-get install -y curl

.prod-rules:
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

deploy:
  script:
    - !reference [.setup-steps, script]
    - ./deploy.sh
  rules: !reference [.prod-rules, rules]
include:template (GitLab CI templates)

GitLab ships hundreds of built-in CI templates at `gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates`. Common ones: `Security/SAST.gitlab-ci.yml`, `Code-Quality.gitlab-ci.yml`, `Docker.gitlab-ci.yml`, `Auto-DevOps.gitlab-ci.yml`.

Examples
include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Secret-Detection.gitlab-ci.yml
  - template: Code-Quality.gitlab-ci.yml

# Override a template job:
semgrep-sast:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
include:inputs

Passes named inputs to a CI component or includable configuration. Components declare their inputs with `spec:inputs`; callers provide values via `inputs:`. This replaces the pattern of overriding template variables.

Examples
spec:
  inputs:
    stage:
      default: test
    image:
      default: alpine

---
my-job:
  stage: $[[ inputs.stage ]]
  image: $[[ inputs.image ]]
  script: echo "running"
Hidden jobs (.job-name)

Jobs prefixed with `.` are never picked up by the runner directly. They serve as abstract bases for `extends` or as anchor definitions. Use them to define shared configurations without creating actual pipeline jobs.

Examples
.docker-login:
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

.only-main:
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

build-image:
  extends:
    - .docker-login
    - .only-main
  script: docker build -t $CI_REGISTRY_IMAGE .
extends: [multiple]

A job can extend multiple hidden jobs or base configurations. GitLab merges them left-to-right with later entries taking priority. Avoids deep YAML nesting while composing pipeline building blocks.

Examples
.base-node:
  image: node:20
  cache:
    key:
      files: [package-lock.json]
    paths: [node_modules/]

.mr-only:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

test-on-mr:
  extends:
    - .base-node
    - .mr-only
  script: npm test
Security (6)
id_tokens (OIDC)

Issues an OpenID Connect JWT for the job. Use to authenticate against cloud providers (AWS, GCP, Azure) or Vault without storing long-lived secrets. The JWT contains claims like `sub` (project path + ref) and is valid only for the job duration.

Examples
deploy-aws:
  id_tokens:
    AWS_OIDC_TOKEN:
      aud: https://sts.amazonaws.com
  script:
    - export AWS_ROLE_ARN=arn:aws:iam::123456789012:role/GitLabCI
    - aws sts assume-role-with-web-identity
        --role-arn $AWS_ROLE_ARN
        --web-identity-token $AWS_OIDC_TOKEN
        --role-session-name gitlab-ci
Protected branches / environments

Variables can be scoped to protected branches and protected environments in GitLab Settings. Only jobs running on those branches/tags/environments can access the variable. Use to prevent production secrets from leaking to feature-branch pipelines.

Examples
# In GitLab UI: Settings → CI/CD → Variables
# Variable: PROD_DB_URL
# Environments: production  ← scoped
# Protected: ✓  ← only runs on protected branches

deploy-prod:
  environment: production
  script: ./deploy.sh $PROD_DB_URL  # only available here
secrets (Vault integration)

Fetches secrets from HashiCorp Vault using JWT authentication. The `vault` keyword requires the GitLab Vault integration (GitLab Premium or the community JWT auth method). Secrets are injected as environment variables.

Examples
deploy:
  secrets:
    DATABASE_PASSWORD:
      vault: production/db/password@ops
    API_KEY:
      vault:
        engine:
          name: kv-v2
          path: secrets
        path: myapp/prod
        field: api_key
  script: ./deploy.sh
environment:action: stop

Defines a teardown job that stops a review environment. The `stop` job must have `when: manual` or `when: always` and reference the same environment name. GitLab can trigger it automatically when an MR is merged.

Examples
deploy-review:
  script: kubectl apply -f k8s/review.yaml
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    on_stop: stop-review

stop-review:
  script: kubectl delete namespace $CI_COMMIT_REF_SLUG
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  when: manual
CI_JOB_TOKEN allowlist

By default, `$CI_JOB_TOKEN` only authenticates against its own project. Use **Settings → CI/CD → Token Access** to grant specific projects access. This prevents token abuse across projects in a group.

Examples
# Reference another project's API using allowlisted CI_JOB_TOKEN:
fetch-shared-lib:
  script:
    - curl --header "JOB-TOKEN: $CI_JOB_TOKEN"
        "$CI_API_V4_URL/projects/456/repository/files/lib.sh/raw?ref=main"
        -o lib.sh
SAST / Secret Detection templates

GitLab-provided security scanning templates. Include them to get automatic vulnerability reports in MRs and the Security Dashboard. `SAST.gitlab-ci.yml` analyzes source code; `Secret-Detection.gitlab-ci.yml` finds credentials in git history.

Examples
include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Secret-Detection.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml
  - template: Security/Container-Scanning.gitlab-ci.yml
Optimization (7)
interruptible

Marks a job as safe to cancel when a newer pipeline starts for the same ref. When `interruptible: true` on all jobs, GitLab auto-cancels older in-progress pipelines on push, saving runner minutes on outdated commits.

Examples
default:
  interruptible: true   # applies to all jobs

# Override for deploy jobs that must not be interrupted:
deploy-prod:
  interruptible: false
  script: ./deploy.sh
resource_group

Prevents concurrent runs of jobs sharing the same resource group name. When a job starts, it acquires an exclusive lock on the group. Subsequent pipeline jobs with the same group queue and run in order. Essential for serializing deploys.

Examples
deploy-prod:
  script: ./deploy.sh production
  resource_group: production-deploy
  environment: production

# Only one deploy-prod can run at a time across all pipelines
[skip ci] / [ci skip]

Including `[skip ci]` or `[ci skip]` in the commit message prevents a pipeline from being created. Useful for documentation-only commits, version bumps, or automated commits that should not trigger CI.

Examples
git commit -m "chore: update changelog [skip ci]"

# Or use the CI_SKIP variable in API triggers:
curl --request POST \
  --form "token=$CI_JOB_TOKEN" \
  --form "ref=main" \
  --form "variables[CI_SKIP]=true" \
  "https://gitlab.example.com/api/v4/projects/1/trigger/pipeline"
Cache policy: pull

Sets `policy: pull` on jobs that only read the cache (e.g., parallel test jobs that all need the same `node_modules`). These jobs skip the cache-upload step, reducing S3/MinIO traffic and job duration.

Examples
install:
  script: npm ci
  cache:
    key:
      files: [package-lock.json]
    paths: [node_modules/]
    policy: pull-push   # download + upload

test:
  script: npm test
  needs: [install]
  cache:
    key:
      files: [package-lock.json]
    paths: [node_modules/]
    policy: pull        # download only — don't re-upload
Sparse checkout / shallow clone

Control git clone depth with `GIT_DEPTH` to speed up jobs on repos with long history. `GIT_STRATEGY: clone|fetch|none` controls whether the runner does a full clone, incremental fetch, or skips cloning entirely (use with artifact-only jobs).

Examples
variables:
  GIT_DEPTH: 1               # shallow clone — fastest
  GIT_STRATEGY: fetch        # reuse existing checkout

deploy:
  variables:
    GIT_STRATEGY: none       # no checkout needed, uses artifacts only
  script: ./deploy.sh
DAG pipeline pattern

Full DAG pipeline: build fans out to multiple parallel test types, then integration tests need all unit tests, and finally deploy needs all tests. No stages needed — `needs` drives the execution order.

Examples
stages: [build, test, deploy]

build-app:
  stage: build
  script: npm run build

test-unit:
  stage: test
  needs: [build-app]
  script: npm test

test-lint:
  stage: test
  needs: [build-app]
  script: npm run lint

test-e2e:
  stage: test
  needs: [test-unit, test-lint]
  script: npm run test:e2e

deploy:
  stage: deploy
  needs: [test-e2e]
  script: ./deploy.sh
rules:if with $CI_COMMIT_MESSAGE

Use `$CI_COMMIT_MESSAGE` in rules to create conditional pipelines based on commit message keywords. Useful for triggering extended test suites, forcing a release build, or skipping expensive jobs from commits.

Examples
full-test-suite:
  script: npm run test:all
  rules:
    - if: '$CI_COMMIT_MESSAGE =~ /\[full-tests\]/'
    - if: '$CI_COMMIT_BRANCH == "main"'

nightly-security-scan:
  script: ./security-scan.sh
  rules:
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
HTTP API (8)
POST /projects/:id/trigger/pipeline

Triggers a pipeline via the API using a pipeline trigger token. Variables can be passed as form fields. Use in external systems (GitHub Actions, webhooks, Slack bots) to start GitLab CI pipelines programmatically.

Examples
curl -X POST \
  --form "token=$TRIGGER_TOKEN" \
  --form "ref=main" \
  --form "variables[DEPLOY_ENV]=production" \
  "https://gitlab.example.com/api/v4/projects/123/trigger/pipeline"
GET /projects/:id/pipelines

Lists pipelines for a project. Filter by `status` (created/waiting_for_resource/preparing/pending/running/success/failed/canceled/skipped/manual/scheduled), `ref`, `sha`, `username`, `source`, `updated_after`.

Examples
# List recent failed pipelines on main:
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
  "https://gitlab.example.com/api/v4/projects/123/pipelines?ref=main&status=failed&per_page=10"
GET /projects/:id/jobs/:job_id/artifacts

Downloads a job's artifact archive. Use the optional `artifact_path` query param to download a specific file instead of the entire archive. Requires authentication or a public project.

Examples
# Download the entire artifact archive:
curl --header "PRIVATE-TOKEN: $TOKEN" \
  "https://gitlab.example.com/api/v4/projects/123/jobs/456/artifacts" \
  -o artifacts.zip

# Download a specific file:
curl --header "PRIVATE-TOKEN: $TOKEN" \
  "https://gitlab.example.com/api/v4/projects/123/jobs/456/artifacts/dist/app.js" \
  -o app.js
GET /projects/:id/pipelines/:pipeline_id/jobs

Lists all jobs for a pipeline. Useful for CI/CD reporting scripts, dashboards, and finding job IDs for subsequent artifact downloads. Filter by `scope` to get only failed, running, or manual jobs.

Examples
# List all failed jobs in a pipeline:
curl --header "PRIVATE-TOKEN: $TOKEN" \
  "https://gitlab.example.com/api/v4/projects/123/pipelines/789/jobs?scope[]=failed"
POST /projects/:id/pipeline

Creates a pipeline for a specific ref without a trigger token (uses a personal/group/project access token). Supports passing variables as a JSON array. Preferred over the trigger endpoint for authenticated scripts.

Examples
curl -X POST \
  --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "ref": "main",
    "variables": [
      {"key": "DEPLOY_ENV", "value": "production"},
      {"key": "IMAGE_TAG", "value": "v1.2.3"}
    ]
  }' \
  "https://gitlab.example.com/api/v4/projects/123/pipeline"
POST /projects/:id/jobs/:job_id/play

Manually triggers a `when: manual` job via the API. Use in release automation scripts to promote artifacts through environments without clicking the GitLab UI.

Examples
# Trigger a manual deploy job:
curl -X POST \
  --header "PRIVATE-TOKEN: $TOKEN" \
  "https://gitlab.example.com/api/v4/projects/123/jobs/456/play"
GET /projects/:id/pipelines/:id (status polling)

Gets the status of a specific pipeline. Poll this endpoint to wait for a triggered pipeline to complete. Check the `status` field: `success`, `failed`, `canceled`, `running`, `pending`.

Examples
#!/bin/bash
PIPELINE_ID=$1
while true; do
  STATUS=$(curl -s --header "PRIVATE-TOKEN: $TOKEN" \
    "https://gitlab.example.com/api/v4/projects/123/pipelines/$PIPELINE_ID" \
    | jq -r '.status')
  echo "Status: $STATUS"
  [[ "$STATUS" =~ ^(success|failed|canceled)$ ]] && break
  sleep 10
done
POST /projects/:id/statuses/:sha

Sets a commit status from an external system. Useful for integrating third-party CI tools, code quality gates, or security scanners with GitLab's commit status API so their results appear in MRs.

Examples
curl -X POST \
  --header "PRIVATE-TOKEN: $TOKEN" \
  --data "state=success&name=external-quality-check&description=All checks passed" \
  "https://gitlab.example.com/api/v4/projects/123/statuses/$CI_COMMIT_SHA"

What this tool does

Searchable GitLab CI/CD cheat sheet with 80+ entries across ten sections. Pipeline structure: `.gitlab-ci.yml` skeleton, `stages`, `workflow:rules`, `default`, global `include`, multi-project pipelines. Job configuration: `script`, `before_script`, `after_script`, `allow_failure`, `retry`, `timeout`, `when`, `environment`. Rules: `rules:if` with predefined variables, `rules:changes`, `rules:exists`, `needs` DAG, merge-request pipelines, `only`/`except`. Variables: job-level and global variables, predefined `CI_*` vars, masked and protected variables, `dotenv` artifacts. Artifacts: `artifacts:paths`, `artifacts:reports` (JUnit, coverage, SAST), `artifacts:expire_in`, `cache` with `key` and `policy`. Runners: `tags`, Docker `image`, `services`, `parallel`, `parallel:matrix`. Templates: `extends`, `!reference`, `include:template`, `include:project`, `include:component`, YAML anchors. Security: protected variables, environments, `id_tokens` (OIDC), masked variables, secrets from HashiCorp Vault / GCP / AWS. Optimization: `interruptible`, `resource_group`, `needs` for DAG, cache strategies, `inherit:variables`, skipping pipelines. HTTP API: trigger pipeline, list jobs, download artifacts, pipeline status. Every entry has bilingual text, copy-ready examples, and pitfall callouts. Search, category chips, one-click copy — all in-browser.

Tool details

Input
Text
The page exposes text boxes, numeric controls, file pickers, or structured inputs depending on the tool.
Output
Live result + Copy
The result area focuses on usable output, with copy, download, or preview actions when supported.
Privacy
Browser-side processing
The main tool logic does not call an external API, so inputs normally stay in the current tab.
Save / share
Shareable URL state
Key settings are encoded in the URL so another person can reopen the same setup.
Performance budget
Initial JS <= 52 KB
No WASM budget is declared, keeping the tool quick to open on mobile.
Best fit
Developer & DevOps · Developer
Category and role tags drive related tools, internal links, and quick fit checks.

How to use

  1. 1. Input

    Paste or drop your content into the tool panel.

  2. 2. Process

    Click the button. All processing is local in your browser.

  3. 3. Copy / Download

    Copy the result or download to disk in one click.

How GitLab CI Cheatsheet fits into your work

Use it in the small gaps between coding, reviewing, debugging, and shipping.

Developer jobs

  • Formatting, validating, shrinking, or inspecting code-adjacent text.
  • Preparing snippets for documentation, tickets, commits, or handoff.
  • Checking a small payload quickly without switching tools.

Developer checks

  • Run irreversible transforms like minify or obfuscate on a copy.
  • Keep secrets out of pasted snippets unless the tool explicitly stays local.
  • Use your normal tests or linter before shipping transformed code.

Good next steps

These links move the current task into a more complete workflow.

  1. 1 Loki Cheatsheet Grafana Loki cheat sheet — 80+ entries covering LogQL stream selectors, line filters, parsers, metric queries, aggregations, Promtail stages, Loki config, and HTTP API. Open
  2. 2 Prometheus Cheatsheet Prometheus cheat sheet — 90+ entries covering PromQL selectors, aggregations, functions, alerting rules, recording rules, HTTP API, and relabeling. Open
  3. 3 kubectl Cheatsheet kubectl cheat sheet — 100+ Kubernetes commands with real examples, common pitfalls, and YAML snippets. Open

Real-world use cases

  • Debugging a pipeline that runs on every push but should only run on main

    You keep seeing pipelines fire on feature branches. Open the cheat sheet, grab the `workflow:rules` block with `$CI_COMMIT_BRANCH == "main"`, paste it at the top of your `.gitlab-ci.yml`, and the problem is gone in one commit. The cheat sheet also shows the MR-pipeline variant and the combined rule that runs on both main and MR events.

  • Setting up a parallel test matrix for multiple Node versions

    Your CI takes 20 minutes because tests run sequentially. Open the cheat sheet, copy the `parallel:matrix` example with `NODE_VERSION: [16, 18, 20]`, paste it into your test job, and all three versions run concurrently. Total time drops from 20 min to 7 min.

  • Passing a computed version number from a build job to a deploy job

    Your build job generates a Docker image tag. Open the cheat sheet, find the `dotenv` artifact entry, copy the two-step pattern (write to `.env` → `artifacts:reports:dotenv`), and the deploy job picks up `$IMAGE_TAG` automatically via `needs`. No sed hacks, no pipeline triggers.

Common pitfalls

  • Using `only: [main]` instead of `rules:if: '$CI_COMMIT_BRANCH == "main"'` — `only` doesn't work correctly with merge-request pipelines and may cause duplicate runs.

  • Putting artifacts and cache in the wrong job — cache is for reusing dependencies across runs; artifacts are for passing files between jobs in the same pipeline.

  • Forgetting `needs` when using DAG — without `needs`, all jobs in a stage still wait for the previous stage to complete, nullifying any parallelism benefit.

  • Using `when: always` on deploy jobs — this causes deploys even after failed tests. Use `when: on_success` (the default) or add `rules:if` guards.

  • Setting CI_*_ predefined variables as job variables — you cannot override most GitLab-managed `CI_` prefixed variables; use custom names instead.

Privacy

Everything runs in your browser. The cheat sheet is a static in-memory array and the search box, category chips, and copy button never make a network request. Nothing you type is logged or sent anywhere. Works offline or on a jump host.

FAQ

Tool combos

Folks in your role tend to reach for these alongside this tool.

Made by Toolora · 100% client-side · Updated 2026-07-01