Skip to content

GitLab CI/CD Keywords and Usage | Complete Reference Guide

GitLab CI/CD provides a rich set of keywords and conditions to control pipeline execution. This guide covers essential keywords, stage conditions, and usage patterns for effective pipeline management.

The core keyword that defines what commands to execute in a job.

build_job:
script:
- echo "Building application"
- npm install
- npm run build

Execute commands before and after the main script.

build_job:
before_script:
- echo "Preparing environment"
- export NODE_ENV=production
script:
- npm run build
after_script:
- echo "Cleaning up"
- rm -rf temp/

Specify the Docker image to use for the job.

build_job:
image: node:18-alpine
script:
- npm run build
build_with_specific_image:
image:
name: node:18
entrypoint: [""]
script:
- npm run build

Additional Docker containers to run alongside the job.

test_with_database:
image: node:18
services:
- postgres:13
- redis:6-alpine
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
script:
- npm run test:integration

Define the order of stages in the pipeline.

stages:
- prepare
- build
- test
- security
- deploy
- cleanup

Assign a job to a specific stage.

prepare_env:
stage: prepare
script:
- echo "Preparing environment"
build_app:
stage: build
script:
- echo "Building application"
test_app:
stage: test
script:
- echo "Testing application"

Create reusable job templates using .job_name syntax.

.deploy_template: &deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
script:
- echo "Deploying to $ENVIRONMENT"
deploy_staging:
<<: *deploy
stage: deploy
variables:
ENVIRONMENT: staging
only:
- develop
deploy_production:
<<: *deploy
stage: deploy
variables:
ENVIRONMENT: production
only:
- main

Job runs only when all previous jobs succeed.

deploy_job:
stage: deploy
when: on_success # This is the default
script:
- echo "Deploying after successful build and test"

Job runs regardless of previous job status.

cleanup_job:
stage: cleanup
when: always
script:
- echo "Cleaning up resources"
- rm -rf temp/
- docker system prune -f

Job never runs automatically (must be triggered manually).

emergency_rollback:
stage: deploy
when: never
script:
- echo "Rolling back to previous version"
- kubectl rollout undo deployment/myapp

Job runs only when at least one previous job fails.

notify_failure:
stage: notify
when: on_failure
script:
- echo "Build failed, sending notification"
- curl -X POST -H 'Content-type: application/json' --data '{"text":"Build failed!"}' $SLACK_WEBHOOK

Job requires manual intervention to start.

deploy_production:
stage: deploy
when: manual
script:
- echo "Deploying to production"
environment:
name: production

Job starts after a specified delay.

deploy_with_delay:
stage: deploy
when: delayed
start_in: 30 minutes
script:
- echo "Deploying after delay"

Control when jobs run based on branches, tags, or other conditions.

# Using only
deploy_production:
script:
- echo "Deploying to production"
only:
- main
- /^release\/.*$/
# Using except
test_job:
script:
- npm test
except:
- schedules
- triggers
# Complex conditions
complex_job:
script:
- echo "Complex conditions"
only:
refs:
- main
- develop
variables:
- $DEPLOY_ENABLED == "true"
changes:
- src/**/*
- package.json

More powerful alternative to only and except.

# Basic rules
deploy_job:
script:
- echo "Deploying"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_TAG'
# Complex rules with when conditions
test_job:
script:
- npm test
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: always
- if: '$CI_COMMIT_BRANCH == "develop"'
when: manual
- changes:
- "src/**/*"
when: on_success
- when: never
# Rules with variables
conditional_deploy:
script:
- echo "Conditional deployment"
rules:
- if: '$DEPLOY_TO_STAGING == "true"'
variables:
ENVIRONMENT: staging
- if: '$DEPLOY_TO_PRODUCTION == "true"'
variables:
ENVIRONMENT: production
when: manual

Allow job to fail without affecting pipeline status.

experimental_test:
script:
- npm run experimental-tests
allow_failure: true
optional_security_scan:
script:
- security-scan
allow_failure:
exit_codes:
- 2
- 3

Save files and directories after job completion.

build_job:
script:
- npm run build
artifacts:
name: "build-$CI_COMMIT_SHORT_SHA"
paths:
- dist/
- build/
exclude:
- "**/*.tmp"
- "dist/debug/"
expire_in: 1 week
when: on_success
reports:
junit: test-results.xml
coverage: coverage.xml

Control which artifacts are downloaded.

test_job:
dependencies:
- build_job
script:
- npm test
deploy_job:
dependencies:
- build_job
script:
- deploy.sh

Advanced job dependencies for parallel execution.

build_backend:
stage: build
script:
- build-backend.sh
build_frontend:
stage: build
script:
- build-frontend.sh
test_integration:
stage: test
needs:
- build_backend
- build_frontend
script:
- integration-tests.sh

Deploy to specific environments with tracking.

deploy_staging:
script:
- deploy.sh staging
environment:
name: staging
url: https://staging.myapp.com
on_stop: stop_staging
deploy_production:
script:
- deploy.sh production
environment:
name: production
url: https://myapp.com
auto_stop_in: 1 week
stop_staging:
script:
- stop-environment.sh staging
environment:
name: staging
action: stop
when: manual

Define job-specific variables.

# Global variables
variables:
GLOBAL_VAR: "global_value"
NODE_VERSION: "18"
# Job-specific variables
build_job:
variables:
BUILD_ENV: production
OPTIMIZE: "true"
script:
- echo "Building with $BUILD_ENV environment"
- echo "Optimization: $OPTIMIZE"
# Conditional variables with rules
deploy_job:
script:
- deploy.sh
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
variables:
ENVIRONMENT: production
- if: '$CI_COMMIT_BRANCH == "develop"'
variables:
ENVIRONMENT: staging

Run multiple instances of the same job.

test_parallel:
script:
- echo "Running test chunk $CI_NODE_INDEX of $CI_NODE_TOTAL"
- npm run test:chunk:$CI_NODE_INDEX
parallel: 5
# Matrix jobs
test_matrix:
script:
- npm run test
parallel:
matrix:
- NODE_VERSION: ["16", "18", "20"]
OS: ["ubuntu", "alpine"]

Control when pipelines run.

workflow:
rules:
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
when: never
- if: '$CI_COMMIT_BRANCH'
# Scheduled pipelines
scheduled_cleanup:
script:
- cleanup-old-artifacts.sh
only:
- schedules

Trigger downstream pipelines.

trigger_downstream:
trigger:
project: mygroup/downstream-project
branch: main
strategy: depend
# Multi-project pipeline
deploy_microservices:
trigger:
include:
- project: mygroup/user-service
file: .gitlab-ci.yml
- project: mygroup/order-service
file: .gitlab-ci.yml

Specify which runners to use.

build_gpu:
tags:
- gpu
- linux
script:
- train-model.py
deploy_kubernetes:
tags:
- kubernetes
- production
script:
- kubectl apply -f deployment.yaml

Ensure only one job runs at a time for a resource.

deploy_production:
resource_group: production
script:
- deploy-to-production.sh
migrate_database:
resource_group: production
script:
- migrate-database.sh

Cache dependencies between jobs.

build_job:
cache:
key:
files:
- package-lock.json
- yarn.lock
paths:
- node_modules/
- .yarn-cache/
policy: pull-push
script:
- npm ci
- npm run build
test_job:
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull
script:
- npm test

Generate OIDC tokens for secure authentication.

deploy_aws:
id_tokens:
AWS_TOKEN:
aud: https://aws.amazon.com
script:
- aws sts assume-role-with-web-identity --role-arn $AWS_ROLE_ARN --role-session-name gitlab-ci --web-identity-token $AWS_TOKEN
smart_deploy:
script:
- |
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
echo "Deploying to production"
deploy-production.sh
elif [ "$CI_COMMIT_BRANCH" = "develop" ]; then
echo "Deploying to staging"
deploy-staging.sh
else
echo "Feature branch deployment"
deploy-preview.sh
fi
rules:
- if: '$CI_COMMIT_BRANCH =~ /^(main|develop|feature\/.*)$/'
when: on_success
- when: never
generate_tests:
stage: prepare
script:
- |
for service in $(ls services/); do
cat >> dynamic_jobs.yml << EOF
test_${service}:
stage: test
script:
- cd services/${service}
- npm test
EOF
done
artifacts:
paths:
- dynamic_jobs.yml
include:
- local: dynamic_jobs.yml
# In .gitlab-ci-template.yml
.deploy_template:
image: alpine:latest
before_script:
- apk add --no-cache curl kubectl
script:
- kubectl apply -f k8s/
environment:
name: $ENVIRONMENT
url: $DEPLOYMENT_URL
# In main .gitlab-ci.yml
include:
- local: '.gitlab-ci-template.yml'
deploy_staging:
extends: .deploy_template
variables:
ENVIRONMENT: staging
DEPLOYMENT_URL: https://staging.myapp.com
only:
- develop
deploy_production:
extends: .deploy_template
variables:
ENVIRONMENT: production
DEPLOYMENT_URL: https://myapp.com
when: manual
only:
- main

This comprehensive guide covers the essential GitLab CI/CD keywords and usage patterns for building robust and flexible pipelines.