GitLab CI is one of the most powerful, fully-integrated CI/CD systems available in 2026 — no external services needed, pipelines defined in a single .gitlab-ci.yml file. This guide builds a complete pipeline from testing to production deployment.
📋 Table of Contents
How GitLab CI Works
GitLab CI runs pipelines defined in .gitlab-ci.yml at your repo root. Each pipeline has stages (test, build, deploy) that run sequentially. Within a stage, jobs run in parallel. Jobs execute on runners (GitLab-hosted or self-hosted).
Basic Pipeline Structure
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
NODE_VERSION: "20"
# Test stage
test:
stage: test
image: node:20-alpine
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- node_modules/
script:
- npm ci
- npm run lint
- npm run test:coverage
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
Build Stage with Docker
build:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
- docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" .
- docker tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" "$CI_REGISTRY_IMAGE:latest"
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
- docker push "$CI_REGISTRY_IMAGE:latest"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
Deploy Stage with Environments
deploy_production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
script:
- ssh -o StrictHostKeyChecking=no $DEPLOY_USER@$PROD_HOST
"cd /app && docker compose pull && docker compose up -d"
environment:
name: production
url: https://example.com
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual # require manual approval for production
Caching for Faster Pipelines
cache:
key:
files:
- package-lock.json # cache invalidates when lockfile changes
paths:
- node_modules/
- .npm/
# Python: paths: [.cache/pip, venv/]
# Go: paths: [.go/pkg/mod/]
# Rust: paths: [target/, .cargo/]
Parallel Matrix Builds
test:
stage: test
parallel:
matrix:
- NODE_VERSION: ["18", "20", "22"]
image: node:$NODE_VERSION-alpine
script:
- npm ci
- npm test
# Runs 3 parallel jobs testing against Node 18, 20, 22
Managing Secrets and Variables
Never hardcode secrets. Store them in Settings, then CI/CD, then Variables:
- Mark sensitive values as Masked (hidden in logs)
- Mark deployment secrets as Protected (only available on protected branches)
- Use Environment scopes for different staging vs production values
Pipeline Optimization Tips
- Use
needs:to run jobs out of stage order when dependencies allow - Use
rules:instead ofonly/except(modern, more flexible) - Use
interruptible: trueso new pushes cancel outdated pipelines - Cache aggressively — dependency install is often the slowest step
- Use small base images (alpine variants) for faster job startup
Frequently Asked Questions
Q: GitLab CI vs GitHub Actions?
A: GitLab CI is fully integrated with GitLab (registry, environments, security scanning built in). GitHub Actions has a larger marketplace of pre-built actions. Both are excellent; choose based on where your code lives.
Q: Do I need self-hosted runners?
A: GitLab-hosted runners work for most projects. Self-hosted runners are worth it for heavy workloads, specific hardware, or private network access.
Q: How do I speed up slow pipelines?
A: Cache dependencies, use needs: for parallel execution, use smaller Docker images, split test suites with parallel:. Profile slowest jobs first.
Q: How do I handle database migrations in the pipeline?
A: Run migrations as a deploy-stage job before restarting the app. Always test migrations in staging first with a production-like database.
Q: Cache vs artifacts — what’s the difference?
A: cache speeds jobs by reusing files across runs (not guaranteed). artifacts pass files between stages within a pipeline (guaranteed).
Conclusion
GitLab CI provides a complete, integrated CI/CD system in a single YAML file. The pipeline here — test with coverage, build and push Docker images, deploy to production with manual approval — covers the full workflow for most applications. Start with a test stage, add build and deploy incrementally, and optimize with caching and parallel jobs as your pipeline grows.
📚 You might also like
🔗 Share this article




✍️ Leave a Comment