GitLab CI/CD Multi-Stage Pipeline Examples
Learn how to design practical GitLab CI/CD pipelines with multiple stages such as build, test, security, package, and deploy. This guide includes beginner examples, artifact handoff, parallel jobs, needs , manual deployment, and real-world best practices.
Table of contents
- Quick answer
- What is a multi-stage pipeline?
- Basic build-test-deploy example
- Passing files between stages
- Parallel jobs and needs
- Node.js multi-stage example
- Docker image pipeline example
- Manual deployment example
- Common mistakes
- Best practices
- Related Melochip guides
Quick answer
Stages define order
Use stages to define high-level flow such as build, test, package, and deploy.
Jobs do the work
Each job belongs to a stage and runs scripts using a GitLab Runner.
Artifacts pass output
Use artifacts when one stage creates files needed by another stage.
needs speeds pipelines
Use needs so jobs can start as soon as required jobs finish.
What is a GitLab multi-stage pipeline?
A multi-stage pipeline is a CI/CD workflow split into logical stages. Each stage groups related jobs. For example, a typical application pipeline might use:
- build — compile code or prepare dependencies
- test — run unit tests, lint checks, and security scans
- package — create build output, artifacts, or Docker images
- deploy — deploy to development, staging, or production
GitLab runs stages in the order you define. Jobs inside the same stage can run in parallel if runners are available.
Basic build-test-deploy example
stages: - build - test - deploy build_app: stage: build script: - echo "Building application" test_app: stage: test script: - echo "Running tests" deploy_app: stage: deploy script: - echo "Deploying application"
This is the simplest multi-stage structure. The deploy stage runs only after the build and test stages complete successfully.
Passing files between stages with artifacts
When a build job creates files that a later job needs, use artifacts. For example, a build job can create a dist/ folder, and the deploy job can use it later.
stages: - build - deploy build_app: stage: build script: - mkdir -p dist - echo "Build output" > dist/index.html artifacts: paths: - dist/ expire_in: 1 week deploy_app: stage: deploy script: - echo "Deploying files" - ls dist/
Parallel jobs and needs
Jobs in the same stage can run in parallel. This is useful when unit tests, lint checks, and security scans do not depend on each other.
stages: - build - test - package build_app: stage: build script: - echo "Building app" artifacts: paths: - dist/ unit_tests: stage: test script: - echo "Running unit tests" lint_code: stage: test script: - echo "Running lint" package_app: stage: package needs: - job: build_app artifacts: true - unit_tests - lint_code script: - echo "Packaging app" - ls dist/
The needs keyword lets GitLab understand the direct dependencies between jobs. This can make pipelines faster because jobs do not always need to wait for every job in a previous stage.
Node.js multi-stage pipeline example
image: node:20 stages: - install - test - build - deploy cache: key: files: - package-lock.json paths: - .npm/ install_dependencies: stage: install script: - npm ci --cache .npm --prefer-offline run_tests: stage: test script: - npm test lint_code: stage: test script: - npm run lint build_app: stage: build script: - npm run build artifacts: paths: - dist/ expire_in: 1 week deploy_dev: stage: deploy script: - echo "Deploying dist folder to dev" - ls dist/ environment: name: development rules: - if: '$CI_COMMIT_BRANCH == "develop"'
This example uses cache for npm dependencies and artifacts for the final build output.
Docker image pipeline example
For containerized applications, the package stage can build and push a Docker image.
stages: - test - build-image run_tests: stage: test image: node:20 script: - npm ci - npm test build_docker_image: stage: build-image image: docker:27 services: - docker:27-dind variables: DOCKER_TLS_CERTDIR: "/certs" IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" script: - echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin - docker build -t "$IMAGE_TAG" . - docker push "$IMAGE_TAG" rules: - if: '$CI_COMMIT_BRANCH == "main"'
This pattern is useful before deploying to Kubernetes or another container platform.
Manual deployment stage example
For staging or production, manual deployment gives humans control before making changes live.
deploy_production: stage: deploy script: - echo "Deploying to production" environment: name: production rules: - if: '$CI_COMMIT_BRANCH == "main"' when: manual - when: never
Common mistakes
| Mistake | Problem | Better approach |
|---|---|---|
| Too many stages | Pipeline becomes slow and hard to read. | Keep stages logical and simple. |
| No artifacts for build output | Deploy jobs cannot find required files. | Use artifacts for output folders. |
| Using cache as artifact | Cache is not reliable for required job output. | Use cache for dependencies only. |
| No manual production gate | Risky code can deploy automatically. | Use when: manual . |
| No clear job names | Pipeline graph is confusing. | Use readable names like run_tests . |
| Missing rules | Jobs run on wrong branches or events. | Add branch or merge request rules. |
Best practices
- Start with simple stages: build, test, package, deploy.
- Use clear job names.
- Run independent test jobs in parallel.
- Use artifacts for files needed by later jobs.
- Use cache for dependency speed.
- Use needs to reduce pipeline waiting time.
- Use manual jobs for production deployment.
- Use rules to control branch, merge request, and scheduled pipeline behavior.
- Keep secrets out of .gitlab-ci.yml .
- Validate YAML before merging pipeline changes.
Screenshot checklist
You can add screenshots later from a safe sample project:
- Pipeline graph showing multiple stages.
- Parallel jobs running in the test stage.
- Artifact download from build job.
- Manual deployment button.
- Job dependency graph using needs .
- Successful pipeline after all stages complete.
- GitLab CI/CD Learning Path
- GitLab CI/CD guides for Beginners
- GitLab CI/CD YAML Syntax Explained
- GitLab Artifacts vs Cache Explained
- GitLab CI/CD Rules, Only, Except, and Workflow Explained
- GitLab CI/CD Docker Build Pipeline
Official references
- GitLab CI/CD YAML syntax reference
- GitLab job artifacts documentation
- GitLab job control documentation
Conclusion
Multi-stage pipelines make GitLab CI/CD easier to understand and scale. Start with build, test, package, and deploy stages. Then add artifacts, cache, rules, manual jobs, and needs as your pipeline grows.
The best pipeline is not the most complex one. It is the one that is easy to read, fast enough for developers, safe for production, and reliable during release.
Join the discussion
0 CommentsAsk questions, add context, or share what worked.
More from Melochip Editorial
Learn what Playwright testing is, why modern QA teams use it, how testers can get started, and the major types...
7 min read · Sep 19, 2026
Learn how Kubernetes Deployments and ReplicaSets work with practical YAML, scaling, self-healing, rolling upda...
7 min read · Sep 17, 2026
Learn Kubernetes security best practices with RBAC, Secrets, ServiceAccounts, Pod securityContext, NetworkPoli...
6 min read · Sep 17, 2026Related articles
Learn Docker registries, image tags, Docker Hub, private registries, login, push, pull, naming rules, CI/CD us...
2 min read · Sep 17, 2026
Learn Docker environment variables and secrets, including docker run -e, env files, Compose environment, secre...
2 min read · Sep 17, 2026
Learn what Playwright testing is, why modern QA teams use it, how testers can get started, and the major types...
7 min read · Sep 19, 2026
Learn how Kubernetes Deployments and ReplicaSets work with practical YAML, scaling, self-healing, rolling upda...
7 min read · Sep 17, 2026Stay in the loop. Get new Melochip articles
Receive Melochip article updates when new articles are available. Explicit opt-in only.
No comments yet. Start the discussion.