DEV Community

Cover image for GitLab-CI
Omar Ahmed
Omar Ahmed

Posted on

GitLab-CI

GitLab Docs: CI/CD YAML syntax reference

gitlab cicd file name

In GitLab, the CI/CD pipeline configuration file is always named:

.gitlab-ci.yml
Enter fullscreen mode Exit fullscreen mode
  • It must be placed in the root directory of your repository.

template .gitlab-ci.yml

pipeline
stages
job1:
    before_script
        step
    script
        step
    after_script
        step
job2:
    script
        step
Enter fullscreen mode Exit fullscreen mode
workflow:
  name: Test
  rules: 
    - if: $CI_COMMIT_BRANCH == 'main'

stages:
  - test
  - deploy

test_job:
  stage: test
  tags: 
    - saas-linux-samll-amd64
  before_script:
    - chmod +x test.sh
  script:
    - ./test.sh
  after_script:
    - ls

deploy_job:
  stage: deploy
  script:
    - touch test.txt

Enter fullscreen mode Exit fullscreen mode

Multi Lines in the step

workflow:
  name: Test

test_job:
  before_script:
    - chmod +x test.sh
  script:
    - ./test.txt
    - >
      this is a test pipeline
      so do not take it seriously > test.txt
  after_script:
    - echo "done"
Enter fullscreen mode Exit fullscreen mode

stages

Use stages to define stages that contain groups of jobs. Use stage in a job to configure the job to run in a specific stage.
If stages is not defined in the .gitlab-ci.yml file, the default pipeline stages are:

  • .pre
  • build
  • test
  • deploy
  • .post

The order of the items in stages defines the execution order for jobs:

  • Jobs in the same stage run in parallel.
  • Jobs in the next stage run after the jobs from the previous stage complete successfully.

If a pipeline contains only jobs in the .pre or .post stages, it does not run. There must be at least one other job in a different stage.

Keyword type: Global keyword.

Example of stages:

stages:
  - build
  - test
  - deploy
Enter fullscreen mode Exit fullscreen mode

In this example:

  • All jobs in build execute in parallel.
  • If all jobs in build succeed, the test jobs execute in parallel.
  • If all jobs in test succeed, the deploy jobs execute in parallel.
  • If all jobs in deploy succeed, the pipeline is marked as passed. If any job fails, the pipeline is marked as failed and jobs in later stages do not start. Jobs in the current stage are not stopped and continue to run.

Artifacts - Storing Job Data

Use artifacts to specify which files to save as job artifacts. Job artifacts are a list of files and directories that are attached to the job when it succeeds, fails, or always.

The artifacts are sent to GitLab after the job finishes. They are available for download in the GitLab UI

By default, jobs in later stages automatically download all the artifacts created by jobs in earlier stages. You can control artifact download behavior in jobs with dependencies.

When using the needs keyword, jobs can only download artifacts from the jobs defined in the needs configuration.

job:
  artifacts:
    name: "job1-artifacts-file"
    when: on_success
    expire_in: 1 week
    paths:
      - binaries/
      - .config
Enter fullscreen mode Exit fullscreen mode

This example stores all files in binaries/, but not *.o files located in subdirectories of binaries/.

artifacts:
  paths:
    - binaries/
  exclude:
    - binaries/**/*.o
Enter fullscreen mode Exit fullscreen mode

Using needs Keyword

You can use the needs keyword to create dependencies between jobs in a pipeline. Jobs run as soon as their dependencies are met, regardless of the pipeline’s stages configuration. You can even configure a pipeline with no stages defined (effectively one large stage) and jobs still run in the proper order.

Working with Variables

Variables can be defined in a CI/CD job, or as a top-level (global) keyword to define default CI/CD variables for all jobs.

1- job variables:

review_job:
  variables:
    DEPLOY_SITE: "https://dev.example.com/"
    REVIEW_PATH: "/review"
  script:
    - deploy-review-script --url $DEPLOY_SITE --path $REVIEW_PATH
Enter fullscreen mode Exit fullscreen mode

2- Default variables:

variables:
  DEPLOY_SITE: "https://example.com/"

deploy_job:
  stage: deploy
  script:
    - deploy-script --url $DEPLOY_SITE --path "/"
  environment: production

deploy_review_job:
  stage: deploy
  variables:
    DEPLOY_SITE: "https://dev.example.com/"
    REVIEW_PATH: "/review"
  script:
    - deploy-review-script --url $DEPLOY_SITE --path $REVIEW_PATH
  environment: production
Enter fullscreen mode Exit fullscreen mode

In this example:

  • deploy_job has no variables defined. The default DEPLOY_SITE variable is copied to the job and can be used in the script section.
  • deploy_review_job already has a DEPLOY_SITE variable defined, so the default DEPLOY_SITE is not copied to the job. The job also has a REVIEW_PATH job variable defined. Both job variables can be used in the script section.

Masking Variables using CICD Settings

masking a variable means hiding its value in the job logs so it doesn’t get exposed when printed (for example, passwords, API tokens, or other secrets).
1- Go to your project in GitLab > Settings → CI/CD → Variables > Add variable > Key: MY_SECRET_TOKEN > Value: the secret value

  • Enable:
    • Masked → hides it in logs
    • Protected (optional) → makes it available only on protected branches/tags

2- Use the variable in your .gitlab-ci.yml:

stages:
  - test

print-secret:
  stage: test
  script:
    - echo "Using secret token..."
    - echo "$MY_SECRET_TOKEN"
Enter fullscreen mode Exit fullscreen mode

the logs will show something like:

Using secret token...
[MASKED]
Enter fullscreen mode Exit fullscreen mode

Predefined CI/CD Variables

Predefined variables become available at three different phases of pipeline execution:

  • Pre-pipeline: Pre-pipeline variables are available before the pipeline is created. These variables are the only variables that can be used with include:rules to control which configuration files to use when creating the pipeline.
  • Pipeline: Pipeline variables become available when GitLab is creating the pipeline. Along with pre-pipeline variables, pipeline variables can be used to configure rules defined in jobs, to determine which jobs to add to the pipeline.
  • Job-only: These variables are only made available to each job when a runner picks up the job and runs it.
  • Predefined variables
print-predefined:
  stage: test
  script:
    - echo "Running on branch: $CI_COMMIT_BRANCH"
    - echo "Commit SHA: $CI_COMMIT_SHA"
    - echo "Pipeline ID: $CI_PIPELINE_ID"

Enter fullscreen mode Exit fullscreen mode

GitLab Merge Requests

A Merge Request proposes changes from one branch into another (e.g., feature/foo → main).

  • Merge Request & rules at Job level: Use rules to include or exclude jobs in pipelines.
  • the keyword rules at the job level controls when a job should run (or not run) based on conditions like branch, pipeline source, variables, etc

Rules are evaluated when the pipeline is created, and evaluated in order. When a match is found, no more rules are checked and the job is either included or excluded from the pipeline depending on the configuration. If no rules match, the job is not added to the pipeline.

rules accepts an array of rules. Each rules must have at least one of:

  • if
  • changes
  • exists
  • when
merge_request_predefined_variables:
  rules:
  # CI_PIPELINE_SOURCE tells you why a pipeline was created.It’s a predefined variable that GitLab automatically sets for every job.
  # https://docs.gitlab.com/ci/jobs/job_rules/#ci_pipeline_source-predefined-variable
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  script: |
    echo "CI_MERGE_REQUEST_LABELS - $CI_MERGE_REQUEST_LABELS"
    echo "CI_MERGE_REQUEST_TARGET_BRANCH_NAME - $CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
    echo "CI_MERGE_REQUEST_ASSIGNEES - $CI_MERGE_REQUEST_ASSIGNEES"
    echo "CI_MERGE_REQUEST_SOURCE_BRANCH_NAME - $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"
    echo "CI_MERGE_REQUEST_TITLE - $CI_MERGE_REQUEST_TITLE"
Enter fullscreen mode Exit fullscreen mode
job_main_branch:
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
  script:
    - echo "I run only on main"
Enter fullscreen mode Exit fullscreen mode

Use rules at Workflow Level

The rules keyword in workflow is similar to rules defined in jobs, but controls whether or not a whole pipeline is created. When no rules evaluate to true, the pipeline does not run.

Supported values: You can use some of the same keywords as job-level rules:

  • rules: if.
  • rules: changes.
  • rules: exists.
  • when, can only be always or never when used with workflow.
  • variables.
workflow:
  rules:
    - if: $CI_COMMIT_TITLE =~ /-draft$/ # =~: match, $: end
      when: never
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == 'main'
       variables:
           DEPLOY_VARIABLE: PRODUCTION
Enter fullscreen mode Exit fullscreen mode

In this example, pipelines run if the commit title (first line of the commit message) does not end with -draft and the pipeline is for either:

  • A merge request
  • The main branch, Then set a job-level variable DEPLOY_VARIABLE with the value "PRODUCTION"
workflow:
  name: Exploring GitLab CI Concepts
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      variables:
        DEPLOY_VARIABLE: "PRODUCTION"

    - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/ && $CI_PIPELINE_SOURCE == "merge_request_event"' # ^: start with feature word
      changes:
        - README.md
      variables:
        DEPLOY_VARIABLE: "TESTING"

deploy-job:
  script:
    - echo "Deploying application..."
    - echo "Application successfully deployed to $DEPLOY_VARIABLE environment"
Enter fullscreen mode Exit fullscreen mode

What this does:

  • First rule → if branch is main, it sets DEPLOY_VARIABLE=PRODUCTION.
  • Second rule → if source branch starts with feature and pipeline comes from a merge request and README.md changed, then DEPLOY_VARIABLE=TESTING. Job output → prints the deployment target environment (PRODUCTION or TESTING).

Resource Groups

  • Use resource_group to create a resource group that ensures a job is mutually exclusive across different pipelines for the same project.
  • For example, if multiple jobs that belong to the same resource group are queued simultaneously, only one of the jobs starts. The other jobs wait until the resource_group is free.
  • If two jobs have the same resource group, GitLab runs them one after the other (queue).
  • If they have different resource groups, they can run at the same time.
  • Process modes
migrate_db:
  stage: deploy
  resource_group: production
  script: ./migrate.sh

deploy_app:
  stage: deploy
  resource_group: production
  script: ./deploy.sh
Enter fullscreen mode Exit fullscreen mode
  • Both jobs affect the same production database/app.
  • If two pipelines run migrations + deploy at the same time → chaos.
  • By sharing resource_group: production, GitLab ensures these always run one after another, never overlapping.

Resource groups control concurrency across pipelines:

  • Pipelines run in parallel when multiple developers push or open merge requests.
  • Two pipelines can both reach the deploy stage at the same time.
  • Stages don’t prevent different pipelines from overlapping.

Example problem:

  • Alice pushes → Pipeline A runs → reaches deploy.
  • Bob pushes → Pipeline B runs at the same time → also reaches deploy.
  • Both deploy jobs run together → race condition (two versions deploying to the same server).

Resource groups solve this:

  • If Pipeline A is deploying, Pipeline B’s deploy job is queued until A finishes.
  • Prevents overlapping deployments to the same environment.
  • Works across all pipelines, not just within one. ### Job Timeout
sleep-job:
  stage: test
  script:
    - echo "Starting long process..."
    - sleep 120   # pretend this takes 2 minutes
    - echo "Process finished!"
  timeout: 1 minute
Enter fullscreen mode Exit fullscreen mode

What happens:

  • The job runs sleep 120 (2 minutes).
  • But the job has timeout: 1 minute.
  • After 1 minute, GitLab kills the job with a timeout error.

Parallel Matrix

parallel matrix in GitLab CI/CD lets you run the same job multiple times in parallel with different variable values

This runs 3 jobs in parallel, one for each Python version:

test_python:
  stage: test
  image: python:$PYTHON_VERSION
  script:
    - python --version
    - pytest
  parallel:
    matrix:
      - PYTHON_VERSION: ["3.8", "3.9", "3.10"]
Enter fullscreen mode Exit fullscreen mode

This expands to 4 jobs:

  • Node 16 + Ubuntu
  • Node 16 + Alpine
  • Node 18 + Ubuntu
  • Node 18 + Alpine
test_node:
  stage: test
  script:
    - node --version
    - npm test
  parallel:
    matrix:
      - NODE_VERSION: ["16", "18"]
        OS: ["ubuntu", "alpine"]
Enter fullscreen mode Exit fullscreen mode

3 parallel jobs → each runs tests against a different database:

integration_tests:
  stage: test
  script:
    - ./run_tests.sh --db=$DB
  parallel:
    matrix:
      - DB: ["mysql", "postgres", "sqlite"]
Enter fullscreen mode Exit fullscreen mode

Skip Pipeline

If your commit message contains [ci skip] or [skip ci], GitLab won’t create a pipeline.

git commit -m "Update README [ci skip]"
git push
Enter fullscreen mode Exit fullscreen mode

skip jobs if the only change is in README.md:

job_build:
  stage: build
  script:
    - echo "Building app..."
  rules:
    - changes:
        - "README.md"
      when: never   # don't run if only README changed
    - when: on_success   # run for everything else
Enter fullscreen mode Exit fullscreen mode

Top comments (0)