Skip to main content

GitLab CI/CD: .gitlab-ci.yml Config & Pipeline Guide

GitLab CI/CD lives directly in your repository as a single .gitlab-ci.yml file. From defining stages and caching dependencies to deploying to multiple environments with manual approval gates, this guide covers the full pipeline authoring workflow with copy-ready examples.

✓ Stages, jobs, rules & variables ✓ GitLab Runners & executors ✓ Cache vs artifacts explained ✓ Include templates & !reference ✓ GitLab CI vs GitHub Actions
Share this tool: X / Twitter LinkedIn
✓ 60,000+ developers use DevPlaybook ★★★★★ 4.8 / 5 on Gumroad ✓ MIT licensed · Instant download ✓ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • ⚡ AI Code Review, Doc Generator & SQL Builder
  • ⚡ All premium templates & early access
  • ⚡ Member discounts on Gumroad products
Start Free Trial →

$9/mo after trial · Cancel anytime

Want the full toolkit?

Get DevPlaybook Pro

Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro — $79 →

MIT licensed · Instant download · No subscription

See what's included → Browse all deals →

.gitlab-ci.yml Structure

Every GitLab CI pipeline starts with a .gitlab-ci.yml in your repo root. Define global stages, then each job specifies which stage it belongs to, its script, and any rules controlling when it runs.

# .gitlab-ci.yml
stages:
  - install
  - test
  - build
  - deploy

variables:
  NODE_VERSION: "20"
  DOCKER_DRIVER: overlay2

cache:
  key: "$CI_COMMIT_REF_SLUG"
  paths:
    - node_modules/
    - .npm/

install-deps:
  stage: install
  image: node:20-alpine
  script:
    - npm ci --cache .npm --prefer-offline
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

unit-tests:
  stage: test
  image: node:20-alpine
  needs: [install-deps]
  script:
    - npm run test:ci
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
  artifacts:
    reports:
      junit: coverage/junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

deploy-staging:
  stage: deploy
  script:
    - ./scripts/deploy.sh staging
  environment:
    name: staging
    url: https://staging.example.com
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

DAG Pipelines with needs

The needs keyword unlocks directed acyclic graph (DAG) execution. A job can start the moment its listed dependencies pass — even if other jobs in the same stage are still running. This can cut pipeline time by 30–60% on complex pipelines.

# DAG: build-backend and build-frontend run in parallel
# deploy only needs build-frontend to finish, not build-backend
build-backend:
  stage: build
  script: make build-api
  artifacts:
    paths: [bin/api]

build-frontend:
  stage: build
  script: npm run build
  artifacts:
    paths: [dist/]

deploy-frontend:
  stage: deploy
  needs: [build-frontend]   # starts immediately after build-frontend passes
  script: aws s3 sync dist/ s3://my-bucket/
  environment:
    name: production

Include Templates & Reuse

Avoid duplicating pipeline logic across repositories. The include keyword merges external YAML into your pipeline at runtime. Use GitLab's built-in security scanning templates to add SAST, dependency scanning, and container scanning with two lines.

# Include built-in GitLab templates
include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml
  # Include from another project in the same GitLab instance
  - project: 'myorg/ci-templates'
    ref: main
    file: '/templates/node-pipeline.yml'

# Override variables from the included template
sast:
  variables:
    SAST_EXCLUDED_PATHS: "spec, test, tests, tmp"

GitLab CI vs GitHub Actions

Feature GitLab CI GitHub Actions
Config file .gitlab-ci.yml .github/workflows/*.yml
Conditional logic rules (powerful expressions) if conditions (simpler)
Reusable logic include + !reference Reusable Workflows / Actions
Security scanning Built-in SAST/DAST/secret detection Marketplace (CodeQL, Snyk)
Parallel execution parallel + needs (DAG) matrix strategy
Free private minutes 400 min/mo (Free tier) 2,000 min/mo (Free tier)

Frequently Asked Questions

How do I validate my .gitlab-ci.yml before pushing?▼

GitLab has a built-in CI Lint tool at https://gitlab.com/your-group/your-project/-/ci/lint. Paste your YAML config to get instant validation, see the expanded job list after include templates are resolved, and dry-run the pipeline. You can also use the GitLab CLI: glab ci lint. For local validation, the schema is available as a JSON Schema file you can wire into VS Code for inline red squiggles.

What is the difference between stages and needs in GitLab CI?▼

Stages define the order in which groups of jobs run — all jobs in a stage complete before the next stage begins. The 'needs' keyword overrides this, allowing a job to start as soon as its listed dependencies pass, regardless of stage ordering. This creates a DAG (directed acyclic graph) pipeline that can run much faster since you are not waiting for entire stages when only specific jobs are needed.

What are GitLab Runners and how do I choose one?▼

Runners are the agents that execute your pipeline jobs. GitLab.com provides shared runners (Linux, macOS, Windows) on the free tier. Group or project runners are dedicated machines you register with the gitlab-runner binary — ideal for jobs needing specific hardware, large caches, or network access to internal services. For open source projects, shared runners are sufficient. Self-hosted GitLab installations always need their own runners.

How does caching work vs artifacts in GitLab CI?▼

Cache stores files between pipeline runs for the same branch — typically package manager dependencies (node_modules, .pip-cache). Artifacts pass files between jobs within the same pipeline — a build stage producing a dist/ folder that the deploy stage then uses. Set cache.key to '$CI_COMMIT_REF_SLUG' to scope the cache per branch. Artifacts are automatically expired (default 30 days) while caches are replaced on each successful upload.

How do I use include to share pipeline templates?▼

The include keyword pulls in external YAML files, avoiding copy-paste across repositories. Use include.project to reference templates from another GitLab repo, include.remote for URLs, or include.template for GitLab's built-in Auto DevOps templates. All included files are merged with your .gitlab-ci.yml at pipeline creation time. The !reference tag lets you reuse specific sections (scripts, rules) from included files without duplicating them.

GitLab CI vs GitHub Actions — key differences?▼

GitLab CI is tightly integrated with GitLab's merge requests, environments, and security scanning — the entire DevSecOps lifecycle lives in one platform. GitHub Actions has a larger marketplace of community actions and is the natural choice for GitHub repos. GitLab CI's rules keyword is more expressive than GitHub Actions' if conditions for complex branching logic. If you are already on GitLab, GitLab CI is the obvious choice; for GitHub-hosted code, GitHub Actions wins on ecosystem and simplicity.