GitLab
02 / 09

CI/CD Pipelines

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 tags

Runners & 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:integration

Pipeline 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 environment

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free