---
# === IDENTITY ===
id: software/system-design/cicd-pipeline/2026
canonical_question: "How do I design a CI/CD pipeline architecture?"
aliases:
  - "CI/CD pipeline design"
  - "continuous integration continuous deployment architecture"
  - "CI/CD pipeline best practices"
  - "how to set up CI/CD"
  - "CI/CD pipeline components"
  - "build deploy pipeline architecture"
  - "DevOps pipeline design patterns"
  - "GitHub Actions vs GitLab CI vs Jenkins"
entity_type: software_reference
domain: software > system-design > cicd_pipeline
region: global
jurisdiction: global
temporal_scope: 2020-2026

# === VERIFICATION ===
last_verified: 2026-02-23
confidence: 0.93
version: 1.0
first_published: 2026-02-23

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "GitHub Actions reusable workflows GA (2022-01), GitLab CI needs keyword GA (2022-02), SLSA v1.0 (2023-04)"
  next_review: 2026-08-22
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Never store secrets in pipeline YAML or environment variables committed to source control -- use platform secret stores (GitHub Secrets, GitLab CI Variables masked, Vault) with short-lived credentials"
  - "Build artifacts exactly once and promote the same artifact through environments -- never rebuild for staging/production as this breaks reproducibility"
  - "Pipeline feedback must complete in under 10 minutes for unit/lint stages -- DORA elite teams maintain lead time under 1 day; slow pipelines cause developers to skip CI"
  - "Never deploy directly to production without at least one gate (automated test suite, manual approval, or canary verification) -- even with trunk-based development"
  - "Pin all CI tool versions, runner images, and action/orb references to SHA or exact version -- floating tags like @latest or @v3 can break pipelines without warning"
  - "Flaky tests must be quarantined immediately -- a test suite with >2% flake rate erodes trust and causes developers to ignore legitimate failures"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Migrating from one specific CI/CD platform to another (e.g., Jenkins to GitHub Actions)"
    use_instead: "software/migrations/jenkins-to-github-actions/2026"
  - condition: "Kubernetes-specific deployment strategies (blue-green, canary in K8s)"
    use_instead: "software/system-design/kubernetes-deployment-strategies/2026"
  - condition: "Docker container build optimization or multi-stage builds"
    use_instead: "software/debugging/docker-container-wont-start/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: platform
    question: "Which CI/CD platform are you using or considering?"
    type: choice
    options: ["GitHub Actions", "GitLab CI", "Jenkins", "CircleCI", "Azure DevOps", "AWS CodePipeline", "Not decided yet"]
  - key: scale
    question: "What is your team size and deployment frequency target?"
    type: choice
    options: ["Solo/small team (<5), weekly deploys", "Medium team (5-20), daily deploys", "Large team (20+), multiple deploys per day", "Enterprise (100+), continuous deployment"]
  - key: workload
    question: "What type of application are you deploying?"
    type: choice
    options: ["Web application (monolith)", "Microservices", "Mobile app", "Library/package", "Infrastructure/IaC", "Monorepo with multiple services"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/system-design/cicd-pipeline/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-02-23)"

# === RELATED UNITS ===
related_kos:
  depends_on:
    - id: "software/system-design/version-control-branching/2026"
      label: "Git Branching Strategies"
  related_to:
    - id: "software/migrations/jenkins-to-github-actions/2026"
      label: "Jenkins to GitHub Actions Migration"
    - id: "software/migrations/circleci-to-github-actions/2026"
      label: "CircleCI to GitHub Actions Migration"
    - id: "software/migrations/docker-compose-to-kubernetes/2026"
      label: "Docker Compose to Kubernetes Migration"
  solves:
    - id: "software/system-design/deployment-strategies/2026"
      label: "Deployment Strategies (Blue-Green, Canary, Rolling)"
  alternative_to:
    - id: "software/system-design/gitops-argocd/2026"
      label: "GitOps with ArgoCD/Flux"
  often_confused_with:
    - id: "software/system-design/release-management/2026"
      label: "Release Management (feature flags, versioning)"

# === SOURCES (7 authoritative sources) ===
sources:
  - id: src1
    title: "Understanding GitHub Actions"
    author: GitHub
    url: https://docs.github.com/en/actions/about-github-actions/understanding-github-actions
    type: official_docs
    published: 2025-12-01
    reliability: high
  - id: src2
    title: "Pipeline Architecture"
    author: GitLab
    url: https://docs.gitlab.com/ci/pipelines/pipeline_architectures/
    type: official_docs
    published: 2025-11-01
    reliability: high
  - id: src3
    title: "Architecting for Scale"
    author: Jenkins
    url: https://www.jenkins.io/doc/book/scaling/architecting-for-scale/
    type: official_docs
    published: 2025-06-01
    reliability: high
  - id: src4
    title: "DORA Metrics"
    author: Atlassian
    url: https://www.atlassian.com/devops/frameworks/dora-metrics
    type: industry_report
    published: 2025-09-01
    reliability: high
  - id: src5
    title: "CircleCI Concepts"
    author: CircleCI
    url: https://circleci.com/docs/guides/about-circleci/concepts/
    type: official_docs
    published: 2025-10-01
    reliability: high
  - id: src6
    title: "SLSA Framework"
    author: OpenSSF
    url: https://slsa.dev/spec/v1.0/
    type: rfc_spec
    published: 2023-04-01
    reliability: authoritative
  - id: src7
    title: "Deployment Pipeline Anti-Patterns"
    author: Jez Humble
    url: https://continuousdelivery.com/2010/09/deployment-pipeline-anti-patterns/
    type: technical_blog
    published: 2010-09-01
    reliability: high
---

# How to Design a CI/CD Pipeline Architecture

## TL;DR

- **Bottom line**: A well-designed CI/CD pipeline connects source control triggers to isolated runners, executes build/test/security stages with fast feedback, produces signed immutable artifacts, and promotes them through environments with explicit gates -- targeting < 10 min for the fast-feedback loop and < 1 day total lead time.
- **Key tool/command**: `.github/workflows/ci.yml` (GitHub Actions) or `.gitlab-ci.yml` (GitLab CI) -- declarative YAML defining stages, jobs, and deployment gates.
- **Watch out for**: Building artifacts multiple times (once per environment) instead of building once and promoting -- this is the #1 source of "works in staging, fails in prod" bugs.
- **Works with**: GitHub Actions, GitLab CI, Jenkins 2.x+, CircleCI, Azure DevOps, AWS CodePipeline, Tekton. Concepts are platform-agnostic.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Never store secrets in pipeline YAML or version control -- use platform secret stores with short-lived credentials
- Build artifacts exactly once and promote the same binary/image through all environments
- Fast-feedback stages (lint, unit tests, SAST) must complete in under 10 minutes
- Never deploy to production without at least one automated or manual gate
- Pin all CI tool versions, runner images, and action references to SHA or exact version tags
- Quarantine flaky tests immediately -- >2% flake rate erodes CI trust and leads to ignored failures

## Quick Reference

| Component | Role | Technology Options | Scaling Strategy |
|---|---|---|---|
| Source Control Trigger | Detects code changes, initiates pipeline | GitHub webhooks, GitLab push events, Jenkins SCM polling | Event-driven (no polling); branch filters to limit triggers |
| Pipeline Orchestrator | Defines stages, manages job dependencies, gates | GitHub Actions, GitLab CI, Jenkins Pipeline, CircleCI, Tekton | Declarative YAML; parallel job execution; DAG-based scheduling |
| Build Runner / Executor | Executes pipeline jobs in isolated environments | GitHub-hosted runners, GitLab shared runners, Jenkins agents, self-hosted runners | Auto-scaling runner pools; ephemeral containers; spot/preemptible instances |
| Artifact Registry | Stores immutable build outputs (images, packages) | Docker Hub, GitHub Packages, GitLab Container Registry, AWS ECR, Artifactory | Content-addressable storage; geo-replicated registries; retention policies |
| Test Framework | Validates correctness at unit, integration, E2E levels | Jest, pytest, JUnit, Cypress, Playwright | Parallel test sharding; intelligent test selection; flaky test quarantine |
| Security Scanner (SAST/DAST) | Shift-left vulnerability detection | Snyk, Semgrep, Trivy, CodeQL, SonarQube | Run SAST in parallel with unit tests; DAST on staging only |
| Secret Manager | Provides credentials to pipeline without exposure | GitHub Secrets, GitLab CI Variables, HashiCorp Vault, AWS Secrets Manager | OIDC federation for cloud providers; short-lived tokens; no static keys |
| Deployment Controller | Manages rollout strategy to target environments | Kubernetes (kubectl/Helm), ArgoCD, AWS CodeDeploy, Terraform | Canary/blue-green via progressive delivery; automated rollback on metric degradation |
| Artifact Signer / SBOM | Supply chain integrity and provenance | Sigstore/Cosign, SLSA provenance, Syft (SBOM), in-toto attestations | Keyless signing via OIDC; SLSA Level 3 with isolated builders |
| Notification / Observability | Pipeline status feedback and metrics | Slack/Teams webhooks, Datadog CI Visibility, Grafana, DORA dashboards | Track 4 DORA metrics; alert on failure rate spikes; pipeline duration trends |
| Environment Manager | Manages staging, preview, production targets | Kubernetes namespaces, Terraform workspaces, Vercel/Netlify preview deploys | Ephemeral preview environments per PR; auto-cleanup on merge |
| Cache Layer | Speeds up repeated builds by reusing dependencies | GitHub Actions cache, GitLab CI cache, S3-backed caches, Turborepo | Cache by lockfile hash; layer caching for Docker; distributed remote caches |

## Decision Tree

> Full script: [decision-tree.txt](scripts/decision-tree.txt) (27 lines)

```
START: Choose your CI/CD platform
|
+-- Already using GitHub for source control?
|   +-- YES --> GitHub Actions (native integration, largest marketplace)
|   +-- NO |
# ... (see full script)
```

**Scaling decision:**
```
+-- Team size < 5, deploys weekly?
|   +-- Single workflow file, manual deploy gate
|
+-- Team 5-20, deploys daily?
|   +-- Multi-stage pipeline, automated staging deploy, manual prod approval
|
+-- Team 20+, multiple deploys/day?
|   +-- Monorepo: path-based triggers + parallel jobs
|   +-- Microservices: per-service pipelines + shared reusable workflows/templates
|
+-- Enterprise 100+, continuous deployment?
|   +-- GitOps (ArgoCD/Flux) + progressive delivery (Argo Rollouts/Flagger)
|   +-- DORA metrics dashboard + deployment frequency tracking
```

## Step-by-Step Guide

### 1. Define pipeline stages and fast-feedback loop

Structure your pipeline into discrete stages that run in order, with fast checks first. The goal is to catch 80% of issues in the first 5 minutes. [src1] [src2]

```yaml
# Canonical stage ordering (platform-agnostic concept)
stages:
  - lint          # < 1 min: code style, formatting
  - security      # < 2 min: SAST, secret scanning (parallel with lint)
  - build         # < 3 min: compile, bundle, create artifact
  - unit-test     # < 5 min: fast unit tests (parallel shards)
  - integration   # < 10 min: API tests, DB tests
  - staging       # deploy to staging, run smoke tests
  - approval      # manual gate or automated canary check
  - production    # deploy to production
  - post-deploy   # smoke tests, DAST, notification
```

**Verify**: Pipeline stages are sequential; jobs within a stage can run in parallel. Lint + security should complete before build starts.

### 2. Configure source control triggers

Set up event-based triggers that only run relevant pipeline stages. Avoid running full pipelines on every push to every branch. [src1]

```yaml
# GitHub Actions trigger configuration
on:
  pull_request:
    branches: [main, develop]
    paths-ignore:
      - '**.md'
      - 'docs/**'
  push:
    branches: [main]
  release:
    types: [published]
```

**Verify**: Push to a documentation-only branch should NOT trigger the pipeline. Push to main should trigger full pipeline.

### 3. Implement build-once, promote-everywhere

Build your artifact (Docker image, binary, package) exactly once, tag it with the commit SHA, and promote that exact artifact through environments. [src7]

```bash
# Build and tag with commit SHA
docker build -t myapp:${GITHUB_SHA} .
docker tag myapp:${GITHUB_SHA} registry.example.com/myapp:${GITHUB_SHA}
docker push registry.example.com/myapp:${GITHUB_SHA}

# In staging: deploy the exact SHA
kubectl set image deployment/myapp app=registry.example.com/myapp:${GITHUB_SHA}

# In production: promote the SAME image (no rebuild)
kubectl set image deployment/myapp app=registry.example.com/myapp:${GITHUB_SHA}
```

**Verify**: `docker inspect registry.example.com/myapp:${SHA}` returns identical image ID in both staging and production.

### 4. Add automated quality gates

Each environment promotion requires passing a quality gate. Combine automated checks with optional manual approval for production. [src4]

```yaml
# GitHub Actions: require status checks before merge
# Settings > Branches > Branch protection rules > Require status checks:
#   - lint
#   - test
#   - security-scan
# Settings > Environments > production > Required reviewers: 1
```

**Verify**: A PR with failing tests cannot be merged. Production deployment requires explicit approval.

### 5. Integrate security scanning (shift-left)

Add SAST, dependency scanning, and secret detection as parallel stages that run alongside unit tests, not after them. [src6]

```yaml
# Run security checks in parallel with tests
security:
  parallel:
    - sast: semgrep --config=auto .
    - deps: trivy fs --severity HIGH,CRITICAL .
    - secrets: gitleaks detect --source .
    - sbom: syft . -o spdx-json > sbom.json
```

**Verify**: `trivy fs .` returns exit code 0 (no HIGH/CRITICAL vulnerabilities). `gitleaks detect` returns exit code 0 (no leaked secrets).

### 6. Set up DORA metrics tracking

Track the four DORA metrics to measure pipeline health. Elite teams target: deployment frequency multiple times/day, lead time < 1 day, change failure rate < 15%, recovery time < 1 hour. [src4]

```bash
# Key metrics to instrument:
# 1. Deployment Frequency: count deployments per day/week
# 2. Lead Time for Changes: time from commit to production deploy
# 3. Change Failure Rate: % of deployments causing incidents
# 4. Mean Time to Recovery: time from incident to resolution

# Example: track deployment timestamp
echo "deployed_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> deployment_log.csv
echo "commit_sha=${GITHUB_SHA}" >> deployment_log.csv
echo "lead_time_seconds=$(($(date +%s) - $(git log -1 --format=%ct)))" >> deployment_log.csv
```

**Verify**: Dashboard shows deployment frequency trend. Lead time from commit to production is tracked.

## Code Examples

### GitHub Actions: Complete CI/CD Pipeline

> Full script: [github-actions-complete-ci-cd-pipeline.yml](scripts/github-actions-complete-ci-cd-pipeline.yml) (79 lines)

```yaml
# .github/workflows/ci-cd.yml
# Input:  Push to main or PR to main
# Output: Tested, scanned, built artifact deployed to staging/production
name: CI/CD Pipeline
on:
# ... (see full script)
```

### GitLab CI: Complete CI/CD Pipeline

> Full script: [gitlab-ci-complete-ci-cd-pipeline.yml](scripts/gitlab-ci-complete-ci-cd-pipeline.yml) (65 lines)

```yaml
# .gitlab-ci.yml
# Input:  Merge request or push to main
# Output: Tested, scanned, built artifact deployed through environments
stages:
  - validate
# ... (see full script)
```

## Anti-Patterns

### Wrong: Rebuilding artifacts per environment

```yaml
# BAD -- rebuilds for each environment; staging and production binaries may differ
deploy-staging:
  script:
    - docker build -t myapp:staging .  # build #1
    - docker push myapp:staging
    - kubectl apply -f k8s/staging/

deploy-production:
  script:
    - docker build -t myapp:production .  # build #2 -- NOT the same binary!
    - docker push myapp:production
    - kubectl apply -f k8s/production/
```

### Correct: Build once, promote the artifact

```yaml
# GOOD -- single build, same image promoted through environments
build:
  script:
    - docker build -t myapp:${CI_COMMIT_SHA} .  # build once
    - docker push myapp:${CI_COMMIT_SHA}

deploy-staging:
  script:
    - kubectl set image deployment/myapp app=myapp:${CI_COMMIT_SHA}  # same image

deploy-production:
  script:
    - kubectl set image deployment/myapp app=myapp:${CI_COMMIT_SHA}  # same image
```

### Wrong: Storing secrets in pipeline YAML

```yaml
# BAD -- secrets in plaintext, committed to version control
env:
  DATABASE_URL: "postgres://admin:p4ssw0rd@db.example.com:5432/prod"
  AWS_SECRET_ACCESS_KEY: "AKIA..."
```

### Correct: Using platform secret stores with OIDC

```yaml
# GOOD -- secrets injected at runtime, never in source control
jobs:
  deploy:
    permissions:
      id-token: write  # enables OIDC
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/deploy
          aws-region: us-east-1
      # No static credentials -- uses short-lived OIDC token
```

### Wrong: Monolithic pipeline that runs everything sequentially

```yaml
# BAD -- 45-minute sequential pipeline; lint failure blocks everything
steps:
  - run: npm run lint
  - run: npm test
  - run: npm run e2e
  - run: docker build .
  - run: trivy image myapp
  - run: kubectl apply -f k8s/
  # Total: 45 minutes, sequential, no parallelism
```

### Correct: Parallel stages with fast-feedback first

```yaml
# GOOD -- parallel execution, fast feedback in < 5 minutes
jobs:
  lint:           # 1 min, runs immediately
    ...
  security-scan:  # 2 min, runs in parallel with lint
    ...
  unit-test:      # 4 min, runs in parallel with lint
    ...
  build:          # 3 min, waits for lint + test + security
    needs: [lint, unit-test, security-scan]
  e2e-test:       # 10 min, waits for build
    needs: [build]
  deploy:         # 2 min, waits for e2e
    needs: [e2e-test]
  # Total: ~20 min with parallelism (vs 45 min sequential)
```

### Wrong: Using floating version tags for CI tools

```yaml
# BAD -- @v3 could change without warning, breaking your pipeline
- uses: actions/checkout@v3      # could be v3.0.0 today, v3.9.9 tomorrow
- uses: docker/build-push-action@latest  # completely unpinned
```

### Correct: Pinning to exact SHA or version

```yaml
# GOOD -- deterministic, reproducible builds
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.1.1
- uses: docker/build-push-action@v6.12.0  # exact version
```

## Common Pitfalls

- **No caching strategy**: Every build downloads dependencies from scratch, adding 2-5 minutes per run. Fix: Cache `node_modules`, `.m2`, pip cache keyed by lockfile hash. [src1]
- **Flaky tests ignored**: Tests intermittently fail but nobody fixes them, eroding CI trust until developers routinely re-run pipelines. Fix: Quarantine flaky tests immediately; track flake rate as a team metric. [src7]
- **No branch protection**: Developers push directly to main, bypassing CI entirely. Fix: Require status checks (lint, test, security) to pass before merge. Enable branch protection rules. [src1]
- **Secrets in logs**: Pipeline logs expose environment variables or command output containing credentials. Fix: Mark secrets as masked in CI settings; never `echo $SECRET`; audit log output. [src3]
- **No rollback plan**: Production deployment fails with no automated way to revert. Fix: Keep the previous artifact tag; automate rollback on health check failure (e.g., `kubectl rollout undo`). [src4]
- **Over-triggering**: Every push to every branch triggers the full pipeline, wasting runner minutes. Fix: Use path filters, branch filters, and `[skip ci]` conventions. [src2]
- **Shared mutable state in tests**: Integration tests share a database or filesystem, causing ordering-dependent failures. Fix: Use per-test database schemas or containers; clean state before each test. [src7]
- **No pipeline observability**: Teams have no visibility into pipeline duration trends, failure rates, or bottlenecks. Fix: Track the 4 DORA metrics; set up CI dashboards (Datadog CI Visibility, GitHub Actions insights). [src4]

## Diagnostic Commands

```bash
# Check GitHub Actions workflow syntax
gh workflow list
gh run list --limit 5

# View recent pipeline runs and their status
gh run view <run-id> --log-failed

# Check GitLab CI config validity
gitlab-ci-lint .gitlab-ci.yml

# Verify Docker image exists in registry
docker manifest inspect registry.example.com/myapp:${SHA}

# Check Kubernetes deployment rollout status
kubectl rollout status deployment/myapp --timeout=300s

# View Jenkins pipeline stages and duration
curl -s "http://jenkins.example.com/job/myapp/lastBuild/api/json?tree=result,duration,stages[name,status]"

# Measure DORA: deployment frequency (last 30 days)
gh api repos/{owner}/{repo}/deployments --paginate | jq '[.[] | select(.created_at > "2026-01-23")] | length'

# Check for secrets accidentally committed
gitleaks detect --source . --verbose
```

## Version History & Compatibility

| Platform | Current Version | Key Feature | Notes |
|---|---|---|---|
| GitHub Actions | v2 (2024+) | Reusable workflows, OIDC, larger runners | Largest marketplace; 2000+ free minutes/month (public repos unlimited) |
| GitLab CI | 17.x (2025) | CI Components catalog, SLSA provenance | Built-in container registry, SAST, DAST; all-in-one platform |
| Jenkins | 2.479+ (2025) | Declarative Pipeline, Pipeline as Code | Requires self-hosting; 1800+ plugins; highest customization |
| CircleCI | Cloud (2025) | Orbs, intelligent test splitting, Docker layer caching | Managed; strong Docker support; credit-based pricing |
| Azure DevOps | 2025 | YAML pipelines, template expressions | Deep Azure/Microsoft integration; hybrid self-hosted agents |
| Tekton | 0.60+ (2025) | Kubernetes-native, CRD-based pipelines | Cloud-native; steep learning curve; ideal for K8s-first teams |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Building any software project with >1 developer | Solo developer with manual deploys to a single server | Simple shell script or manual deploy |
| You need reproducible, auditable builds | Prototyping or hackathon with no production target | Direct `git push` to hosting (Vercel, Netlify auto-deploy) |
| Compliance requires build provenance (SOC2, SLSA) | The project has no tests and no build step | Add tests first, then add CI/CD |
| Team targets DORA elite metrics (daily deploys, <1h recovery) | Deploying static files with no build process | Static site hosts with git-triggered deploys |
| Microservices with independent release cycles | Tightly coupled monolith where everything deploys together anyway | Single pipeline with all-or-nothing deploy |
| Multiple environments (dev, staging, production) | Single environment with no promotion path | Direct deployment script |

## Important Caveats

- Pipeline YAML syntax and features vary significantly between platforms -- a GitHub Actions workflow is not portable to GitLab CI without rewriting. The architecture concepts (stages, gates, artifact promotion) are portable; the implementation is not.
- Self-hosted runners (Jenkins agents, GitHub self-hosted runners) require patching, monitoring, and security hardening -- they become attack vectors if compromised, as they have access to secrets and deployment credentials.
- DORA metrics are team-level indicators, not individual developer metrics. Using deployment frequency to evaluate individual performance leads to gaming (meaningless micro-deployments) rather than genuine improvement.
- "CI/CD" is often used loosely to mean just CI (automated testing). True CD (continuous deployment to production) requires significant investment in automated testing, monitoring, and rollback capabilities. Most teams practice CI + continuous delivery (manual production gate), not continuous deployment.
- Cost can escalate quickly with managed CI/CD: GitHub Actions charges per-minute for private repos, CircleCI uses credits, and GitLab CI shared runners have quotas. Self-hosted runners trade money for operational overhead.

## Related Units

- [Jenkins to GitHub Actions Migration](/software/migrations/jenkins-to-github-actions/2026)
- [CircleCI to GitHub Actions Migration](/software/migrations/circleci-to-github-actions/2026)
- [Docker Compose to Kubernetes Migration](/software/migrations/docker-compose-to-kubernetes/2026)
- [Docker Container Won't Start](/software/debugging/docker-container-wont-start/2026)
