Setup CI with GitLab Pipelines
How to configure GitLab CI/CD pipelines for testing, building, and deploying applications using .gitlab-ci.yml with stages, jobs, caching, and runners.
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Overview
GitLab CI/CD is a built-in continuous integration and deployment platform that uses a .gitlab-ci.yml file to define pipelines. Jobs run in isolated Docker containers on shared or self-hosted runners, making it easy to automate testing, building, and releasing software.
Before CI/CD pipelines, teams ran tests and deployments manually from local machines. This led to “works on my laptop” bugs, inconsistent environments, and no audit trail of what was deployed when. GitLab CI/CD solves this by codifying every step of the delivery process in version-controlled YAML.
When to Use
Use this recipe when:
- Setting up automated testing for a GitLab-hosted project on every push or merge request.
- Building and pushing Docker images to a registry as part of the release process.
- Deploying to staging or production with environment-specific variables and manual approvals.
- Running scheduled pipelines for nightly backups, dependency audits, or periodic cleanup tasks.
- Using self-hosted runners for private infrastructure or specialized build environments.
Step-by-Step Implementation
Basic Pipeline (Node.js)
# .gitlab-ci.yml
stages:
- test
- build
- deploy
test:
stage: test
image: node:20
script:
- npm ci
- npm run lint
- npm run test
cache:
paths:
- node_modules/
key: ${CI_COMMIT_REF_SLUG}
build:
stage: build
image: node:20
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
deploy:
stage: deploy
image: alpine
script:
- echo "Deploying to production"
environment:
name: production
url: https://app.example.com
only:
- main
Docker Build and Push
stages:
- build
- deploy
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build-docker:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
only:
- main
deploy-staging:
stage: deploy
image: alpine/k8s:1.30.2
script:
- kubectl set image deployment/app app=$DOCKER_IMAGE -n staging
environment:
name: staging
only:
- main
Self-Hosted Runner
# Register a runner on your own server
gitlab-runner register \
--non-interactive \
--url "https://gitlab.com/" \
--registration-token "YOUR_TOKEN" \
--executor "docker" \
--docker-image "alpine:latest" \
--description "self-hosted-runner"
# .gitlab-ci.yml targeting specific runner
build:
stage: build
tags:
- self-hosted-runner
script:
- make build
Matrix Jobs (Parallel Testing)
stages:
- test
test:
stage: test
parallel:
matrix:
- NODE_VERSION: ["18", "20", "22"]
image: node:${NODE_VERSION}
script:
- npm ci
- npm test
Best Practices
- Use
npm ciinstead ofnpm installin CI for reproducible builds that strictly respectpackage-lock.json. - Cache dependencies between jobs using the
cachekeyword to dramatically reduce build times. - Pin Docker image versions instead of using
latesttags to ensure reproducible builds. - Use
artifactsto pass files between stages (e.g., compiled bundles from build to deploy). - Set
onlyorrulescarefully to avoid running expensive deploy jobs on feature branches. - Use
environmentblocks for deployment jobs to track what is deployed and enable rollbacks.
Common Mistakes
- Not caching
node_modulescauses every job to reinstall dependencies from scratch, wasting minutes per run. - Using
onlyinstead ofrules—rulesis the modern, more flexible way to control job execution. - Running DIND without TLS can expose the Docker socket to other jobs on the same runner.
- Storing secrets in
.gitlab-ci.yml— always use CI/CD variables from the project settings. - Forgetting
tagsfor self-hosted runners causes jobs to queue indefinitely on shared runners.
Related Resources
Related Resources
GitHub Actions CI/CD
How to build and deploy with GitHub Actions using workflows, matrices, caching, and secrets.
RecipeDocker Basics
How to containerize an application, write a Dockerfile, and run containers with Docker Compose.
RecipeEnvironment Variables
How to read, set, and manage environment variables securely across Python, JavaScript, and Java.