GitLab CI/CD Pipelines
GitLab CI/CD is configured entirely in .gitlab-ci.yml at the repo root. Pipelines run on Runners — lightweight agents that execute jobs. The declarative YAML syntax covers stages, dependencies, caching, and advanced conditional logic.
.gitlab-ci.yml Fundamentals
# .gitlab-ci.yml — minimal full-stack pipeline example
default:
image: node:20-alpine # Default Docker image for all jobs
before_script:
- npm ci --cache .npm --prefer-offline
cache:
key:
files:
- package-lock.json
paths:
- .npm/
stages:
- test
- build
- deploy
variables:
NODE_ENV: production
ARTIFACT_PATH: dist/
# --- Test stage ---
lint:
stage: test
script:
- npm run lint
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" # Only on MRs
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # And on main
unit-tests:
stage: test
script:
- npm test -- --coverage
coverage: /All files[^|]*|[^|]*s+([d.]+)/ # Parse coverage % from output
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
expire_in: 1 week
# --- Build stage ---
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
needs: ["lint", "unit-tests"] # Run as soon as dependencies pass (DAG)
# --- Deploy stage ---
deploy-staging:
stage: deploy
image: alpine
script:
- apk add --no-cache curl
- curl -X POST $DEPLOY_WEBHOOK_URL
environment:
name: staging
url: https://staging.example.com
needs: ["build"]
rules:
- if: $CI_COMMIT_BRANCH == "main"
deploy-production:
stage: deploy
script:
- ./deploy.sh production
environment:
name: production
url: https://example.com
when: manual # Requires manual trigger in GitLab UI
rules:
- if: $CI_COMMIT_TAG =~ /^vd+.d+.d+$/ # Only on semver tagsRunners & Docker Executor
# Runner types:
# Shared runners: GitLab-hosted; available to all projects; limited free minutes
# Group runners: registered to a group; available to all projects in it
# Project runners: registered to a single project
# Register a self-hosted runner (Docker executor)
# 1. Install GitLab Runner
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
sudo apt-get install gitlab-runner
# 2. Register (get token from Settings → CI/CD → Runners)
sudo gitlab-runner register --url https://gitlab.com --registration-token <TOKEN> --executor docker --docker-image alpine:latest --description "docker-runner-prod" --tag-list "docker,linux" --run-untagged true --locked false
# 3. Start the runner
sudo gitlab-runner start
sudo gitlab-runner status
# Target a specific runner with tags
job-on-gpu:
tags:
- gpu
- docker
script:
- nvidia-smi
# Services (sidecar containers — e.g. test database)
integration-tests:
stage: test
services:
- name: postgres:15
alias: postgres
variables:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
DATABASE_URL: postgresql://test:test@postgres:5432/testdb
script:
- npm run test:integrationPipeline Triggers & Multi-Project Pipelines
# Trigger a downstream project pipeline from upstream
trigger-deploy:
stage: deploy
trigger:
project: mygroup/infrastructure
branch: main
strategy: depend # Wait for downstream pipeline to succeed
# Pass variables to downstream pipeline
trigger-with-vars:
stage: deploy
variables:
DEPLOY_ENV: staging
APP_VERSION: $CI_COMMIT_SHA
trigger:
project: mygroup/infrastructure
# Parent-child pipelines (split large pipelines into separate YAML files)
generate-child:
stage: build
trigger:
include:
- local: ci/frontend.yml
- local: ci/backend.yml
strategy: depend
# Trigger pipeline via API (e.g. from a webhook)
# curl -X POST # --form "token=<trigger-token>" # --form "ref=main" # --form "variables[DEPLOY_ENV]=production" # "https://gitlab.com/api/v4/projects/:id/trigger/pipeline"
# Useful CI/CD predefined variables:
# $CI_COMMIT_SHA — full commit SHA
# $CI_COMMIT_SHORT_SHA — first 8 chars of SHA
# $CI_COMMIT_BRANCH — branch name
# $CI_COMMIT_TAG — tag name (if triggered by a tag)
# $CI_PIPELINE_SOURCE — push / merge_request_event / schedule / trigger / web
# $CI_PROJECT_PATH — mygroup/myproject
# $CI_REGISTRY_IMAGE — container registry image path for this project
# $CI_ENVIRONMENT_NAME — name of the current environmentKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free