Skip to content

GitLab CI/CD

GitLab CI/CD is configured entirely via a single YAML file committed at the root of your repository: .gitlab-ci.yml. Every push triggers the pipeline.

stages:
- build
- test
- deploy
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build:image:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
test:unit:
stage: test
image: node:22-alpine
script:
- npm ci
- npm test
deploy:production:
stage: deploy
script:
- kubectl set image deployment/app app=$IMAGE_TAG
only:
- main
  • Stages define the order of execution. All jobs in a stage run in parallel.
  • Jobs are the atomic units of work. A job fails → the stage fails → pipeline stops (by default).
VariableDescription
$CI_COMMIT_SHAFull commit hash
$CI_COMMIT_SHORT_SHAShort 8-char hash — useful for image tags
$CI_COMMIT_REF_NAMEBranch or tag name
$CI_REGISTRY_IMAGEFull path to the GitLab container registry image
$CI_PROJECT_IDNumeric project ID
$CI_ENVIRONMENT_NAMEName of the deployment environment

Prefer rules: — it’s more expressive and evaluated in order:

deploy:staging:
rules:
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_MERGE_REQUEST_ID
when: never
Test your GitLab CI job locally

$ gitlab-runner exec docker test:unit Running with gitlab-runner 17.0.0 (HEAD) Preparing the “docker” executor… Using Docker executor with image node:22-alpine… Pulling docker image node:22-alpine… Running on runner-local via hostname… Getting source from Git repository… Executing “step_script” stage of the job script $ npm ci added 412 packages in 8s $ npm test PASS src/utils.test.ts Test Suites: 1 passed, 1 total Job succeeded

  • Never echo secrets — use masked CI/CD variables
  • Pin runner images to specific digests, not latest
  • Enable SECURE_ANALYZERS_PREFIX to pull scanners from your mirror registry
  • Use protected variables for production credentials
  • Enable pipeline security reports (SAST, dependency, container scanning)