What Is GitLab CI/CD?

GitLab CI/CD is a built-in continuous integration and delivery platform that's part of GitLab β€” both the SaaS version at gitlab.com and self-hosted GitLab instances. Unlike GitHub Actions (which launched in 2019), GitLab has had CI/CD since 2015, making it one of the most mature pipeline systems available.

Everything is configured via a single file: .gitlab-ci.yml in the root of your repository. When you push a commit, GitLab reads this file and orchestrates the pipeline automatically β€” no external service, no webhook setup.

GitLab vs GitHub: GitLab is a complete DevOps platform β€” it includes source control, CI/CD, container registry, package registry, security scanning, and project management in one product. GitHub achieves similar coverage through integrations and Actions marketplace.

The .gitlab-ci.yml File

The .gitlab-ci.yml file defines your entire pipeline. A minimal working example:

.gitlab-ci.yml YAML
image: node:20-alpine   # default Docker image for all jobs

stages:
  - test
  - build
  - deploy

test:unit:
  stage: test
  script:
    - npm ci
    - npm test

build:app:
  stage: build
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/

deploy:production:
  stage: deploy
  script:
    - ./deploy.sh
  only:
    - main

Stages and Jobs

GitLab pipelines are organised into stages. All jobs in the same stage run in parallel. Stages run sequentially β€” the next stage only begins if all jobs in the previous stage pass.

A job is the basic unit of work. Each job defines:

  • stage: which stage it belongs to
  • script: the shell commands to run
  • image: the Docker image to use (can override the global default)
  • rules: or only:/except: to control when the job runs
  • needs: to create a directed acyclic graph (DAG) for parallel execution without waiting for full stages

DAG pipelines: The needs: keyword lets jobs start as soon as their specific dependencies complete, rather than waiting for an entire stage. For large pipelines this can dramatically reduce total pipeline time.

GitLab Runners

GitLab Runners are agents that pick up and execute jobs. GitLab.com provides shared runners (Linux, Windows, macOS) that are available for all projects. For self-hosted GitLab instances you must provide your own runners.

Runner types:

  • Shared runners: Available to all projects in a GitLab instance. Good for most workloads.
  • Group runners: Available to all projects within a group.
  • Project-specific runners: Registered to a single project. Used for workloads that need specific hardware, environments, or security isolation.

Runners execute jobs in executors β€” the most common is the Docker executor, which spins up a container for each job using the specified image:. The Shell executor runs commands directly on the runner host, which is faster but less isolated.

Self-hosted runner security: Self-hosted runners that pick up jobs from public repositories are a supply chain risk. A malicious merge request could run arbitrary code on your runner. Always register separate runners for public repositories and never give them production credentials.

Artifacts and Caching

Artifacts are files generated by a job that you want to pass to later jobs or download after the pipeline. They're uploaded to GitLab at the end of the job and downloaded by jobs that depend on them.

Caching stores directories between pipeline runs to speed up dependency installation. Unlike artifacts, cache is not guaranteed to be available β€” it's a performance optimisation.

artifacts and cache example YAML
test:unit:
  stage: test
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - node_modules/
  script:
    - npm ci
    - npm test
  artifacts:
    reports:
      junit: junit.xml   # shows test results in MR UI
    expire_in: 1 week

Environments and Deployments

GitLab tracks deployments through the environment: keyword. This creates a deployment history, enables rollbacks, and allows you to require manual approval before deploying to production:

environment deployment YAML
deploy:staging:
  stage: deploy
  environment:
    name: staging
    url: https://staging.example.com
  script: ./deploy.sh staging

deploy:production:
  stage: deploy
  environment:
    name: production
    url: https://example.com
  when: manual   # requires manual click to deploy
  script: ./deploy.sh production
  only:
    - main

Variables and Secrets

GitLab CI/CD variables can be defined at the project, group, or instance level. Masked variables are hidden in job logs. Protected variables are only available to jobs running on protected branches or tags.

For secrets (API keys, tokens, passwords), use masked and protected CI/CD variables. Never commit them to .gitlab-ci.yml. Reference them as $VARIABLE_NAME in your scripts.

Variable inheritance risk: Group-level variables are inherited by all subgroups and projects. Review your group variable scope carefully β€” a token with broad permissions at the group level is exposed to every project in that group.

Real-World Pipeline Example

A complete pipeline for a Python web application with security scanning:

.gitlab-ci.yml (production pipeline) YAML
image: python:3.12-slim

stages:
  - test
  - security
  - build
  - deploy

test:pytest:
  stage: test
  cache:
    paths: [.venv/]
  script:
    - pip install -r requirements.txt
    - pytest --junitxml=report.xml
  artifacts:
    reports:
      junit: report.xml

security:sast:
  stage: security
  image: aquilax/scanner:latest
  script:
    - aquilax scan --fail-on critical
  allow_failure: false   # block pipeline on critical findings

build:docker:
  stage: build
  image: docker:24
  services: [docker:dind]
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

GitLab CI/CD vs GitHub Actions

  • Configuration: GitLab uses a single .gitlab-ci.yml; GitHub Actions uses multiple files in .github/workflows/
  • Runners: GitLab requires runner registration; GitHub provides hosted runners automatically
  • Security scanning: GitLab Ultimate includes built-in SAST, DAST, SCA, and secret detection; GitHub requires Actions or third-party tools
  • Self-hosting: GitLab Community Edition is free and self-hostable; GitHub Enterprise requires a paid licence
  • Marketplace: GitHub has a larger Actions marketplace; GitLab uses reusable CI/CD components and templates

Both are excellent: The right choice depends on your broader tooling. If you're all-in on GitHub for source control and project management, GitHub Actions is the natural choice. If you want a single integrated DevOps platform with built-in security, GitLab is hard to beat.

Add Security to Your GitLab Pipelines

AquilaX integrates with GitLab CI/CD to scan every commit for vulnerabilities, secrets, and misconfigurations β€” with AI-assisted fix suggestions delivered directly in your MRs.

Start Free Scan