Pipelines are defined in .gitlab-ci.yml at the repo root. Jobs run within stages, and stages run in the order
they're declared — every job in one stage completes before the next stage starts, unless you opt out with needs.
Minimal Pipeline
stages:
- build
- test
- deploy
build:
stage: build
script:
- npm ci
- npm run build
test:
stage: test
script:
- npm test
deploy:
stage: deploy
script:
- ./deploy.sh
rules:
- if: '$CI_COMMIT_BRANCH == "main"'Variables
variables:
NODE_ENV: production
deploy:
script:
- echo "Deploying to $CI_ENVIRONMENT_NAME"
environment:
name: production
url: https://example.comPredefined variables
GitLab exposes dozens of built-in variables — $CI_COMMIT_SHA, $CI_COMMIT_BRANCH, $CI_PIPELINE_ID,
$CI_PROJECT_DIR — without any config. Check the predefined variables reference before writing a script step that
reconstructs something GitLab already gives you.
Rules
deploy_staging:
script: ./deploy.sh staging
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
deploy_prod:
script: ./deploy.sh production
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manualrules
The current, flexible way to control when a job runs — conditions, when: manual, when: never, all in one
ordered list evaluated top to bottom.
only / except (legacy)
The older syntax for the same purpose. Still works, but rules supersedes it — new pipelines should use
rules, not a mix of both in the same job.
Caching & Artifacts
build:
script:
- npm ci
- npm run build
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
artifacts:
paths:
- dist/
expire_in: 1 weekCache vs. artifacts
Cache speeds up later pipeline runs by reusing dependencies (node_modules) — it's best-effort and can be empty.
Artifacts pass build output between stages of the same pipeline run and are guaranteed available to downstream
jobs — don't rely on cache for something a later job actually needs to exist.
needs (DAG pipelines)
test:
stage: test
needs: [build]
script: npm testneeds vs. stage order
By default a job waits for every job in the previous stage. needs lets a job start as soon as its specific
dependencies finish, even if other jobs in an earlier stage are still running — turns a strictly sequential
pipeline into a dependency graph.
Runners
gitlab-runner listLists runners registered on this machine.
gitlab-runner registerRegisters a new runner against a GitLab instance, interactively prompting for the registration token and executor.
gitlab-runner verifyChecks that registered runners can still authenticate against GitLab.
Include (shared pipeline config)
include:
- project: 'my-group/ci-templates'
file: '/templates/deploy.yml'
- local: '.gitlab/ci/test.yml'Why include over copy-paste
include lets multiple projects share the same pipeline template from one source — a security scan step or a
standard deploy job gets fixed once centrally instead of being patched in every repo that copied it.