Stage 5 · Platform
Building Pipelines
GitLab CI Pipelines
Stages, needs, rules, includes, child pipelines, and runner tags in .gitlab-ci.yml.
.gitlab-ci.yml Structure
GitLab CI configuration lives in .gitlab-ci.yml at the repository root. The file defines global keywords (stages, variables, default) and job definitions. Jobs are top-level keys that contain their configuration.
default:
image: node:20
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull-push
variables:
NODE_ENV: test
stages:
- build
- test
- deploy
build:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
test:
stage: test
script:
- npm test
deploy:
stage: deploy
script:
- ./deploy.sh
environment:
name: production
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manualThe default keyword applies to all jobs (image, cache). Variables are global unless overridden in a job. Stages define the order. Jobs without an explicit stage go to the test stage by default.
Stages and Needs
GitLab CI uses stages for ordering and needs for fine-grained dependencies. Without needs, jobs in the same stage run in parallel and jobs in the next stage wait for all previous stage jobs. The needs keyword overrides this behavior for DAG scheduling.
stages:
- build
- test
- deploy
build-frontend:
stage: build
script:
- npm run build:frontend
artifacts:
paths: [dist/frontend/]
build-backend:
stage: build
script:
- go build -o bin/server ./cmd/server
artifacts:
paths: [bin/server]
test-frontend:
stage: test
needs: [build-frontend]
script:
- npm test -- --frontend
test-backend:
stage: test
needs: [build-backend]
script:
- go test ./...
deploy:
stage: deploy
needs: [test-frontend, test-backend]
script:
- ./deploy.shtest-frontend only waits for build-frontend, not build-backend. test-backend only waits for build-backend. deploy waits for both test jobs. This reduces total pipeline duration by running independent work in parallel.
Rules and Expressions
Rules replace the older only/except syntax for controlling when jobs run. Rules evaluate conditions in order and execute the job when the first matching rule is found. They support if, changes, exists, and when keywords.
deploy-staging:
script:
- ./deploy.sh staging
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: always
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
allow_failure: true
deploy-production:
script:
- ./deploy.sh production
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: false
- when: never
build:
script:
- npm run build
rules:
- changes:
- "src/**"
- "package.json"
when: on_success
- when: on_success
allow_failure: falseThe staging deploy runs automatically on main and manually on merge requests. The production deploy is manual-only on main. The build job uses changes to detect if source files changed, but still runs on all pushes as a fallback.
Includes and Templates
The includes keyword imports external CI configuration files. You can include local files, remote URLs, templates from the GitLab CI library, or project files from other GitLab projects. This enables sharing common configurations across teams.
include:
- local: "/ci/templates/deploy.yml"
- project: "devops/ci-templates"
file: "/templates/node-ci.yml"
ref: main
- remote: "https://example.com/ci/security-scan.yml"
- template: Security/SAST.gitlab-ci.yml
stages:
- build
- test
- security
- deploy
# Override template defaults
sast:
variables:
SAST_EXCLUDED_PATHS: "vendor/,node_modules/"GitLab includes are merged in order. Local files override remote files, which override templates. You can override included job definitions by redefining them in the main file.
Child Pipelines
Child pipelines are separate pipelines triggered by a parent pipeline. They are useful for splitting large monorepo pipelines into independent, focused pipelines. Each child pipeline runs its own set of jobs and reports status back to the parent.
# .gitlab-ci.yml (parent)
generate-child:
stage: build
script:
- python generate-pipeline.py > child-pipeline.yml
artifacts:
paths:
- child-pipeline.yml
child-pipeline:
stage: build
trigger:
include:
- artifact: child-pipeline.yml
job: generate-child
strategy: dependThe parent pipeline generates a dynamic child pipeline configuration. The child pipeline runs independently and its status is reported back to the parent via strategy: depend.
Runner Tags
Runner tags route jobs to specific runners. Tags are key-value pairs assigned to runners. Jobs specify which tags they need, and GitLab assigns them to a runner with matching tags. This lets you route GPU jobs to GPU runners, Windows jobs to Windows runners, and so on.
gpu-training:
tags:
- gpu
- linux
script:
- python train.py
windows-build:
tags:
- windows
- docker
script:
- npm ci
- npm run build
macos-sign:
tags:
- macos
- security
script:
- xcodebuild -archivePath build.xcarchive
- codesign --sign "Developer ID" build.xcarchiveTags ensure jobs run on runners with the required hardware and software. Without tags, jobs run on any available shared runner, which may not have the tools or capabilities needed.
GitLab allows you to protect tags so only certain users can assign jobs to tagged runners. Use this for jobs that deploy to production or access sensitive credentials — restrict runner access to trusted pipelines.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.