Introduction
Multi-environment pipelines enable safe, progressive deployment of code changes through isolated environments. Each environment serves a specific purpose in validating changes before they reach production users.
This guide visualizes the multi-environment deployment flow:
- Environment Hierarchy: Dev → Staging → Production
- Environment Isolation: Separate configs, databases, resources
- Progressive Promotion: Automated testing at each stage
- Approval Gates: Manual checkpoints for production
- Configuration Management: Environment-specific settings
Part 1: Multi-Environment Architecture
Complete Environment Flow
%%{init: {'theme':'dark', 'themeVariables': {'primaryTextColor':'#e5e7eb','secondaryTextColor':'#e5e7eb','tertiaryTextColor':'#e5e7eb','textColor':'#e5e7eb','nodeTextColor':'#e5e7eb','edgeLabelText':'#e5e7eb','clusterTextColor':'#e5e7eb','actorTextColor':'#e5e7eb'}}}%%
flowchart TD
Dev([👨💻 Developer]) --> LocalDev[Local Development
Laptop/Docker Desktop
Fast iteration] LocalDev --> Push[git push origin feature/new-api] Push --> CI[CI Pipeline Triggered
Build + Test + Lint] CI --> CIPass{CI
Passed?} CIPass -->|No| FixLocal[❌ Fix locally
Check logs
Run tests] FixLocal -.-> LocalDev CIPass -->|Yes| FeatureBranch{Branch
type?} FeatureBranch -->|feature/*| DevEnv[🔧 Dev Environment
Namespace: dev
Auto-deploy on push] FeatureBranch -->|main| StagingEnv[🎯 Staging Environment
Namespace: staging
Auto-deploy on merge] subgraph DevEnvironment[Development Environment] DevEnv --> DevConfig[Configuration:
- Debug mode ON
- Verbose logging
- Mock external APIs
- Dev database
- Minimal replicas: 1] DevConfig --> DevTest[Basic Tests:
- Smoke tests
- Health checks
- Manual QA] DevTest --> DevDone[✅ Dev validated
Ready for staging] end DevDone --> MergePR[Merge Pull Request
to main branch] MergePR --> StagingEnv subgraph StagingEnvironment[Staging Environment] StagingEnv --> StagingConfig[Configuration:
- Production-like setup
- Staging database
- Real external APIs test
- Replicas: 2-3
- Resource limits] StagingConfig --> StagingTest[Comprehensive Tests:
- Integration tests
- E2E tests
- Performance tests
- Security scans] StagingTest --> StagingResult{All tests
passed?} StagingResult -->|No| StagingFail[❌ Staging failed
Rollback staging
Fix issues] StagingFail -.-> FixLocal StagingResult -->|Yes| StagingMonitor[Monitor staging:
- Error rates
- Performance metrics
- User acceptance testing] StagingMonitor --> StagingReady[✅ Staging validated
Ready for production] end StagingReady --> ApprovalGate{Manual
Approval
Required} ApprovalGate --> ReviewTeam[Team Lead Review:
- Code changes
- Test results
- Risk assessment
- Deployment timing] ReviewTeam --> Approved{Approved?} Approved -->|No| Rejected[❌ Rejected
More testing needed
or wrong timing] Approved -->|Yes| ProdEnv[🚀 Production Environment
Namespace: production
Manual trigger only] subgraph ProductionEnvironment[Production Environment] ProdEnv --> ProdConfig[Configuration:
- Production settings
- Production database
- High availability
- Replicas: 5-10
- Strict resource limits
- Auto-scaling enabled] ProdConfig --> ProdDeploy[Deployment Strategy:
- Blue-green or
- Canary or
- Rolling update] ProdDeploy --> ProdHealth{Production
healthy?} ProdHealth -->|No| AutoRollback[🚨 Auto-rollback
Revert to previous
Alert on-call team] ProdHealth -->|Yes| ProdMonitor[Monitor Production:
- Real user metrics
- Error rates
- Business KPIs
- SLO compliance] ProdMonitor --> ProdStable{Stable for
15 minutes?} ProdStable -->|No| AutoRollback ProdStable -->|Yes| Success[✅ Deployment Complete!
New version live
Monitor continues] end style DevEnv fill:#064e3b,stroke:#10b981 style StagingEnv fill:#78350f,stroke:#f59e0b style ProdEnv fill:#1e3a8a,stroke:#3b82f6 style Success fill:#064e3b,stroke:#10b981 style StagingFail fill:#7f1d1d,stroke:#ef4444 style AutoRollback fill:#7f1d1d,stroke:#ef4444 style Rejected fill:#7f1d1d,stroke:#ef4444
Laptop/Docker Desktop
Fast iteration] LocalDev --> Push[git push origin feature/new-api] Push --> CI[CI Pipeline Triggered
Build + Test + Lint] CI --> CIPass{CI
Passed?} CIPass -->|No| FixLocal[❌ Fix locally
Check logs
Run tests] FixLocal -.-> LocalDev CIPass -->|Yes| FeatureBranch{Branch
type?} FeatureBranch -->|feature/*| DevEnv[🔧 Dev Environment
Namespace: dev
Auto-deploy on push] FeatureBranch -->|main| StagingEnv[🎯 Staging Environment
Namespace: staging
Auto-deploy on merge] subgraph DevEnvironment[Development Environment] DevEnv --> DevConfig[Configuration:
- Debug mode ON
- Verbose logging
- Mock external APIs
- Dev database
- Minimal replicas: 1] DevConfig --> DevTest[Basic Tests:
- Smoke tests
- Health checks
- Manual QA] DevTest --> DevDone[✅ Dev validated
Ready for staging] end DevDone --> MergePR[Merge Pull Request
to main branch] MergePR --> StagingEnv subgraph StagingEnvironment[Staging Environment] StagingEnv --> StagingConfig[Configuration:
- Production-like setup
- Staging database
- Real external APIs test
- Replicas: 2-3
- Resource limits] StagingConfig --> StagingTest[Comprehensive Tests:
- Integration tests
- E2E tests
- Performance tests
- Security scans] StagingTest --> StagingResult{All tests
passed?} StagingResult -->|No| StagingFail[❌ Staging failed
Rollback staging
Fix issues] StagingFail -.-> FixLocal StagingResult -->|Yes| StagingMonitor[Monitor staging:
- Error rates
- Performance metrics
- User acceptance testing] StagingMonitor --> StagingReady[✅ Staging validated
Ready for production] end StagingReady --> ApprovalGate{Manual
Approval
Required} ApprovalGate --> ReviewTeam[Team Lead Review:
- Code changes
- Test results
- Risk assessment
- Deployment timing] ReviewTeam --> Approved{Approved?} Approved -->|No| Rejected[❌ Rejected
More testing needed
or wrong timing] Approved -->|Yes| ProdEnv[🚀 Production Environment
Namespace: production
Manual trigger only] subgraph ProductionEnvironment[Production Environment] ProdEnv --> ProdConfig[Configuration:
- Production settings
- Production database
- High availability
- Replicas: 5-10
- Strict resource limits
- Auto-scaling enabled] ProdConfig --> ProdDeploy[Deployment Strategy:
- Blue-green or
- Canary or
- Rolling update] ProdDeploy --> ProdHealth{Production
healthy?} ProdHealth -->|No| AutoRollback[🚨 Auto-rollback
Revert to previous
Alert on-call team] ProdHealth -->|Yes| ProdMonitor[Monitor Production:
- Real user metrics
- Error rates
- Business KPIs
- SLO compliance] ProdMonitor --> ProdStable{Stable for
15 minutes?} ProdStable -->|No| AutoRollback ProdStable -->|Yes| Success[✅ Deployment Complete!
New version live
Monitor continues] end style DevEnv fill:#064e3b,stroke:#10b981 style StagingEnv fill:#78350f,stroke:#f59e0b style ProdEnv fill:#1e3a8a,stroke:#3b82f6 style Success fill:#064e3b,stroke:#10b981 style StagingFail fill:#7f1d1d,stroke:#ef4444 style AutoRollback fill:#7f1d1d,stroke:#ef4444 style Rejected fill:#7f1d1d,stroke:#ef4444
Part 2: Environment Comparison
Environment Characteristics
%%{init: {'theme':'dark', 'themeVariables': {'primaryTextColor':'#e5e7eb','secondaryTextColor':'#e5e7eb','tertiaryTextColor':'#e5e7eb','textColor':'#e5e7eb','nodeTextColor':'#e5e7eb','edgeLabelText':'#e5e7eb','clusterTextColor':'#e5e7eb','actorTextColor':'#e5e7eb'}}}%%
graph TB
subgraph Local[🏠 Local Development]
LocalProps[Properties:
✓ Fast iteration
✓ Developer's laptop
✓ Docker Compose
✓ Mock services
✓ Hot reload enabled] LocalData[Data:
- SQLite or local DB
- Seed data
- No real user data
- Quick reset] LocalAccess[Access:
- localhost only
- No authentication
- Debug tools enabled] end subgraph Dev[🔧 Development Environment] DevProps[Properties:
✓ Shared team env
✓ Kubernetes cluster
✓ Continuous deployment
✓ Latest features
✓ Can be unstable] DevData[Data:
- Dev database
- Synthetic test data
- Reset weekly
- No PII] DevAccess[Access:
- VPN required
- Basic auth
- All developers
- Debug mode ON] end subgraph Staging[🎯 Staging Environment] StagingProps[Properties:
✓ Production mirror
✓ Same infrastructure
✓ Pre-production testing
✓ Stable builds only
✓ Performance testing] StagingData[Data:
- Staging database
- Anonymized prod data
- Or realistic test data
- Refreshed monthly] StagingAccess[Access:
- VPN required
- OAuth/SSO
- Developers + QA
- Debug mode OFF] end subgraph Prod[🚀 Production Environment] ProdProps[Properties:
✓ Live customer traffic
✓ High availability
✓ Auto-scaling
✓ Disaster recovery
✓ Maximum stability] ProdData[Data:
- Production database
- Real user data
- Encrypted at rest
- Regular backups] ProdAccess[Access:
- Public internet
- Full authentication
- Limited admin access
- Audit logging enabled] end Local --> |git push feature/*| Dev Dev --> |Merge to main| Staging Staging --> |Manual approval| Prod style Local fill:#064e3b,stroke:#10b981 style Dev fill:#064e3b,stroke:#10b981 style Staging fill:#78350f,stroke:#f59e0b style Prod fill:#1e3a8a,stroke:#3b82f6
✓ Fast iteration
✓ Developer's laptop
✓ Docker Compose
✓ Mock services
✓ Hot reload enabled] LocalData[Data:
- SQLite or local DB
- Seed data
- No real user data
- Quick reset] LocalAccess[Access:
- localhost only
- No authentication
- Debug tools enabled] end subgraph Dev[🔧 Development Environment] DevProps[Properties:
✓ Shared team env
✓ Kubernetes cluster
✓ Continuous deployment
✓ Latest features
✓ Can be unstable] DevData[Data:
- Dev database
- Synthetic test data
- Reset weekly
- No PII] DevAccess[Access:
- VPN required
- Basic auth
- All developers
- Debug mode ON] end subgraph Staging[🎯 Staging Environment] StagingProps[Properties:
✓ Production mirror
✓ Same infrastructure
✓ Pre-production testing
✓ Stable builds only
✓ Performance testing] StagingData[Data:
- Staging database
- Anonymized prod data
- Or realistic test data
- Refreshed monthly] StagingAccess[Access:
- VPN required
- OAuth/SSO
- Developers + QA
- Debug mode OFF] end subgraph Prod[🚀 Production Environment] ProdProps[Properties:
✓ Live customer traffic
✓ High availability
✓ Auto-scaling
✓ Disaster recovery
✓ Maximum stability] ProdData[Data:
- Production database
- Real user data
- Encrypted at rest
- Regular backups] ProdAccess[Access:
- Public internet
- Full authentication
- Limited admin access
- Audit logging enabled] end Local --> |git push feature/*| Dev Dev --> |Merge to main| Staging Staging --> |Manual approval| Prod style Local fill:#064e3b,stroke:#10b981 style Dev fill:#064e3b,stroke:#10b981 style Staging fill:#78350f,stroke:#f59e0b style Prod fill:#1e3a8a,stroke:#3b82f6
Environment Configuration Matrix
| Aspect | Local | Dev | Staging | Production |
|---|---|---|---|---|
| Purpose | Development | Feature testing | Pre-production validation | Live users |
| Deployment | Manual | Auto on push | Auto on merge | Manual approval |
| Replicas | 1 | 1-2 | 2-3 | 5-10+ |
| Database | Local SQLite | Shared dev DB | Staging DB (prod-like) | Production DB |
| Resources | Minimal | Low | Medium (prod-like) | High |
| Monitoring | None | Basic | Full | Full + Alerts |
| Debug Mode | Yes | Yes | No | No |
| Logging Level | DEBUG | DEBUG | INFO | WARN/ERROR |
| External APIs | Mocked | Test endpoints | Test endpoints | Production endpoints |
| Data | Seed data | Synthetic | Anonymized | Real user data |
| Access | localhost | VPN + Basic auth | VPN + SSO | Public + Full auth |
| Uptime SLA | N/A | None | None | 99.9%+ |
Part 3: Progressive Promotion Pipeline
Promotion Flow with Quality Gates
%%{init: {'theme':'dark', 'themeVariables': {'primaryTextColor':'#e5e7eb','secondaryTextColor':'#e5e7eb','tertiaryTextColor':'#e5e7eb','textColor':'#e5e7eb','nodeTextColor':'#e5e7eb','edgeLabelText':'#e5e7eb','clusterTextColor':'#e5e7eb','actorTextColor':'#e5e7eb'}}}%%
flowchart LR
subgraph LocalStage[Local Stage]
L1[Write Code]
L2[Run Unit Tests]
L3[Manual Testing]
L1 --> L2 --> L3
end
subgraph DevStage[Dev Stage]
D1[Auto Deploy]
D2[Smoke Tests]
D3{Tests
Pass?} D4[Dev Validated ✓] D1 --> D2 --> D3 D3 -->|Yes| D4 D3 -->|No| D5[❌ Fix] D5 -.-> L1 end subgraph StagingStage[Staging Stage] S1[Auto Deploy] S2[Integration Tests] S3[E2E Tests] S4[Performance Tests] S5{All Pass?} S6[Staging Validated ✓] S1 --> S2 --> S3 --> S4 --> S5 S5 -->|Yes| S6 S5 -->|No| S7[❌ Fix] S7 -.-> L1 end subgraph ApprovalStage[Approval Gate] A1[Create Release] A2[Code Review] A3[Change Advisory] A4{Approved?} A1 --> A2 --> A3 --> A4 A4 -->|No| A5[❌ Rejected] A5 -.-> L1 end subgraph ProdStage[Production Stage] P1[Manual Deploy] P2[Canary 10%] P3{Healthy?} P4[Increase to 50%] P5{Healthy?} P6[Complete 100%] P7[Monitor] P8[Success ✓] P1 --> P2 --> P3 P3 -->|Yes| P4 --> P5 P5 -->|Yes| P6 --> P7 --> P8 P3 -->|No| P9[🚨 Rollback] P5 -->|No| P9 end L3 --> |git push| D1 D4 --> |Merge PR| S1 S6 --> A1 A4 -->|Yes| P1 style L3 fill:#064e3b,stroke:#10b981 style D4 fill:#064e3b,stroke:#10b981 style S6 fill:#064e3b,stroke:#10b981 style P8 fill:#064e3b,stroke:#10b981 style D5 fill:#7f1d1d,stroke:#ef4444 style S7 fill:#7f1d1d,stroke:#ef4444 style P9 fill:#7f1d1d,stroke:#ef4444
Pass?} D4[Dev Validated ✓] D1 --> D2 --> D3 D3 -->|Yes| D4 D3 -->|No| D5[❌ Fix] D5 -.-> L1 end subgraph StagingStage[Staging Stage] S1[Auto Deploy] S2[Integration Tests] S3[E2E Tests] S4[Performance Tests] S5{All Pass?} S6[Staging Validated ✓] S1 --> S2 --> S3 --> S4 --> S5 S5 -->|Yes| S6 S5 -->|No| S7[❌ Fix] S7 -.-> L1 end subgraph ApprovalStage[Approval Gate] A1[Create Release] A2[Code Review] A3[Change Advisory] A4{Approved?} A1 --> A2 --> A3 --> A4 A4 -->|No| A5[❌ Rejected] A5 -.-> L1 end subgraph ProdStage[Production Stage] P1[Manual Deploy] P2[Canary 10%] P3{Healthy?} P4[Increase to 50%] P5{Healthy?} P6[Complete 100%] P7[Monitor] P8[Success ✓] P1 --> P2 --> P3 P3 -->|Yes| P4 --> P5 P5 -->|Yes| P6 --> P7 --> P8 P3 -->|No| P9[🚨 Rollback] P5 -->|No| P9 end L3 --> |git push| D1 D4 --> |Merge PR| S1 S6 --> A1 A4 -->|Yes| P1 style L3 fill:#064e3b,stroke:#10b981 style D4 fill:#064e3b,stroke:#10b981 style S6 fill:#064e3b,stroke:#10b981 style P8 fill:#064e3b,stroke:#10b981 style D5 fill:#7f1d1d,stroke:#ef4444 style S7 fill:#7f1d1d,stroke:#ef4444 style P9 fill:#7f1d1d,stroke:#ef4444
Part 4: Environment-Specific Configuration
Configuration Management Strategy
%%{init: {'theme':'dark', 'themeVariables': {'primaryTextColor':'#e5e7eb','secondaryTextColor':'#e5e7eb','tertiaryTextColor':'#e5e7eb','textColor':'#e5e7eb','nodeTextColor':'#e5e7eb','edgeLabelText':'#e5e7eb','clusterTextColor':'#e5e7eb','actorTextColor':'#e5e7eb'}}}%%
flowchart TD
Start([Application needs config]) --> Method{Config
Method?} Method --> EnvVars[Environment Variables] Method --> ConfigMaps[Kubernetes ConfigMaps] Method --> Secrets[Kubernetes Secrets] EnvVars --> EnvExample[Examples:
- NODE_ENV=production
- LOG_LEVEL=info
- FEATURE_FLAGS=true] ConfigMaps --> CMExample[Examples:
- app-config.yaml
- nginx.conf
- application.properties] Secrets --> SecretExample[Examples:
- DATABASE_PASSWORD
- API_KEYS
- TLS certificates] EnvExample --> Override{Override per
environment?} CMExample --> Override SecretExample --> Override Override --> DevOverride[Dev Environment:
DEBUG=true
DB_HOST=dev-db
REPLICAS=1
CACHE_TTL=60s] Override --> StagingOverride[Staging Environment:
DEBUG=false
DB_HOST=staging-db
REPLICAS=3
CACHE_TTL=300s] Override --> ProdOverride[Production Environment:
DEBUG=false
DB_HOST=prod-db
REPLICAS=10
CACHE_TTL=600s] DevOverride --> Inject[Inject at deployment:
kubectl apply -f k8s/dev/
- deployment.yaml
- configmap.yaml
- secrets.yaml] StagingOverride --> Inject ProdOverride --> Inject style EnvVars fill:#1e3a8a,stroke:#3b82f6 style ConfigMaps fill:#1e3a8a,stroke:#3b82f6 style Secrets fill:#7f1d1d,stroke:#ef4444
Method?} Method --> EnvVars[Environment Variables] Method --> ConfigMaps[Kubernetes ConfigMaps] Method --> Secrets[Kubernetes Secrets] EnvVars --> EnvExample[Examples:
- NODE_ENV=production
- LOG_LEVEL=info
- FEATURE_FLAGS=true] ConfigMaps --> CMExample[Examples:
- app-config.yaml
- nginx.conf
- application.properties] Secrets --> SecretExample[Examples:
- DATABASE_PASSWORD
- API_KEYS
- TLS certificates] EnvExample --> Override{Override per
environment?} CMExample --> Override SecretExample --> Override Override --> DevOverride[Dev Environment:
DEBUG=true
DB_HOST=dev-db
REPLICAS=1
CACHE_TTL=60s] Override --> StagingOverride[Staging Environment:
DEBUG=false
DB_HOST=staging-db
REPLICAS=3
CACHE_TTL=300s] Override --> ProdOverride[Production Environment:
DEBUG=false
DB_HOST=prod-db
REPLICAS=10
CACHE_TTL=600s] DevOverride --> Inject[Inject at deployment:
kubectl apply -f k8s/dev/
- deployment.yaml
- configmap.yaml
- secrets.yaml] StagingOverride --> Inject ProdOverride --> Inject style EnvVars fill:#1e3a8a,stroke:#3b82f6 style ConfigMaps fill:#1e3a8a,stroke:#3b82f6 style Secrets fill:#7f1d1d,stroke:#ef4444
Kubernetes Configuration Example
# k8s/base/deployment.yaml (Common base)
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest # Overridden per environment
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: myapp-config
- secretRef:
name: myapp-secrets
resources:
# Overridden per environment
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
---
# k8s/dev/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: dev
data:
NODE_ENV: "development"
LOG_LEVEL: "debug"
DATABASE_HOST: "postgres.dev.svc.cluster.local"
REDIS_HOST: "redis.dev.svc.cluster.local"
FEATURE_NEW_UI: "true"
FEATURE_BETA_API: "true"
---
# k8s/staging/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: staging
data:
NODE_ENV: "staging"
LOG_LEVEL: "info"
DATABASE_HOST: "postgres.staging.svc.cluster.local"
REDIS_HOST: "redis.staging.svc.cluster.local"
FEATURE_NEW_UI: "true"
FEATURE_BETA_API: "false"
---
# k8s/production/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: production
data:
NODE_ENV: "production"
LOG_LEVEL: "warn"
DATABASE_HOST: "postgres.production.svc.cluster.local"
REDIS_HOST: "redis.production.svc.cluster.local"
FEATURE_NEW_UI: "false" # Gradual rollout
FEATURE_BETA_API: "false"
---
# k8s/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev
resources:
- ../base/deployment.yaml
- configmap.yaml
- secrets.yaml
images:
- name: myapp
newTag: dev-abc123
replicas:
- name: myapp
count: 1
patches:
- patch: |-
- op: replace
path: /spec/template/spec/containers/0/resources/requests/memory
value: 128Mi
- op: replace
path: /spec/template/spec/containers/0/resources/limits/memory
value: 256Mi
target:
kind: Deployment
name: myapp
---
# k8s/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
resources:
- ../base/deployment.yaml
- configmap.yaml
- secrets.yaml
images:
- name: myapp
newTag: v1.2.3
replicas:
- name: myapp
count: 10
patches:
- patch: |-
- op: replace
path: /spec/template/spec/containers/0/resources/requests/memory
value: 512Mi
- op: replace
path: /spec/template/spec/containers/0/resources/limits/memory
value: 1Gi
- op: replace
path: /spec/template/spec/containers/0/resources/requests/cpu
value: 500m
- op: replace
path: /spec/template/spec/containers/0/resources/limits/cpu
value: 1000m
target:
kind: Deployment
name: myapp
Part 5: Database Migration Strategy
Multi-Environment Database Flow
%%{init: {'theme':'dark', 'themeVariables': {'primaryTextColor':'#e5e7eb','secondaryTextColor':'#e5e7eb','tertiaryTextColor':'#e5e7eb','textColor':'#e5e7eb','nodeTextColor':'#e5e7eb','edgeLabelText':'#e5e7eb','clusterTextColor':'#e5e7eb','actorTextColor':'#e5e7eb'}}}%%
sequenceDiagram
participant Dev as Developer
participant DevDB as Dev Database
participant StagingDB as Staging Database
participant ProdDB as Production Database
participant Migration as Migration Tool
Note over Dev: Write migration:
001_add_users_table.sql Dev->>DevDB: Run migration locally
CREATE TABLE users... DevDB-->>Dev: Migration applied ✓ Dev->>Dev: Test application
with new schema Dev->>Dev: git push feature/add-users Note over DevDB: CI/CD Pipeline triggered Dev->>DevDB: Auto-run migrations
in dev environment DevDB-->>Dev: Dev DB updated ✓ Note over Dev: Create Pull Request
Merge to main Dev->>StagingDB: Trigger staging deployment Note over Migration,StagingDB: Pre-deployment hook Migration->>StagingDB: Backup database
pg_dump > backup.sql Migration->>StagingDB: Run migrations
001_add_users_table.sql StagingDB-->>Migration: Migration applied ✓ Note over StagingDB: Deploy application
Test with new schema alt Migration Failed Migration->>StagingDB: Rollback migration
Restore from backup StagingDB-->>Migration: Rolled back end Note over Dev: Manual approval
for production Dev->>ProdDB: Trigger production deployment Note over Migration,ProdDB: Pre-deployment steps Migration->>ProdDB: Full database backup
Snapshot created Migration->>ProdDB: Check migration status
SELECT version FROM schema_migrations ProdDB-->>Migration: Current version: 000 Migration->>ProdDB: Run migrations
in transaction Note over Migration,ProdDB: BEGIN;
CREATE TABLE users;
INSERT INTO schema_migrations
VALUES ('001');
COMMIT; ProdDB-->>Migration: Migration successful ✓ Note over ProdDB: Deploy new application
version alt Production Issues Migration->>ProdDB: Rollback migration
Run down migration:
DROP TABLE users; Note over ProdDB: Deploy previous
application version end Migration->>ProdDB: Verify data integrity
Check constraints ProdDB-->>Migration: All checks passed ✓ Note over Dev,ProdDB: Production updated successfully
001_add_users_table.sql Dev->>DevDB: Run migration locally
CREATE TABLE users... DevDB-->>Dev: Migration applied ✓ Dev->>Dev: Test application
with new schema Dev->>Dev: git push feature/add-users Note over DevDB: CI/CD Pipeline triggered Dev->>DevDB: Auto-run migrations
in dev environment DevDB-->>Dev: Dev DB updated ✓ Note over Dev: Create Pull Request
Merge to main Dev->>StagingDB: Trigger staging deployment Note over Migration,StagingDB: Pre-deployment hook Migration->>StagingDB: Backup database
pg_dump > backup.sql Migration->>StagingDB: Run migrations
001_add_users_table.sql StagingDB-->>Migration: Migration applied ✓ Note over StagingDB: Deploy application
Test with new schema alt Migration Failed Migration->>StagingDB: Rollback migration
Restore from backup StagingDB-->>Migration: Rolled back end Note over Dev: Manual approval
for production Dev->>ProdDB: Trigger production deployment Note over Migration,ProdDB: Pre-deployment steps Migration->>ProdDB: Full database backup
Snapshot created Migration->>ProdDB: Check migration status
SELECT version FROM schema_migrations ProdDB-->>Migration: Current version: 000 Migration->>ProdDB: Run migrations
in transaction Note over Migration,ProdDB: BEGIN;
CREATE TABLE users;
INSERT INTO schema_migrations
VALUES ('001');
COMMIT; ProdDB-->>Migration: Migration successful ✓ Note over ProdDB: Deploy new application
version alt Production Issues Migration->>ProdDB: Rollback migration
Run down migration:
DROP TABLE users; Note over ProdDB: Deploy previous
application version end Migration->>ProdDB: Verify data integrity
Check constraints ProdDB-->>Migration: All checks passed ✓ Note over Dev,ProdDB: Production updated successfully
Part 6: Multi-Environment CI/CD Pipeline
Complete Pipeline Configuration
# .github/workflows/multi-env-deploy.yml
name: Multi-Environment Deployment
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# CI - Same for all environments
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run linting
run: npm run lint
- name: Run unit tests
run: npm test
- name: Build Docker image
run: docker build -t $IMAGE_NAME:${{ github.sha }} .
- name: Run integration tests
run: docker-compose -f docker-compose.test.yml up --abort-on-container-exit
- name: Push image
run: |
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push $IMAGE_NAME:${{ github.sha }}
# Deploy to Dev - Auto on feature branches
deploy-dev:
needs: build-and-test
if: github.ref != 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: development
url: https://dev.example.com
steps:
- uses: actions/checkout@v3
- name: Deploy to Dev
run: |
kubectl config set-cluster dev --server="${{ secrets.DEV_K8S_SERVER }}"
kubectl config set-credentials admin --token="${{ secrets.DEV_K8S_TOKEN }}"
kubectl set image deployment/myapp myapp=$IMAGE_NAME:${{ github.sha }} -n dev
kubectl rollout status deployment/myapp -n dev
- name: Run smoke tests
run: |
curl https://dev.example.com/health
npm run test:smoke -- --env=dev
# Deploy to Staging - Auto on main branch
deploy-staging:
needs: build-and-test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v3
- name: Run database migrations
run: |
kubectl exec -n staging deployment/postgres -- \
psql -U postgres -d app -f /migrations/migrate.sql
- name: Deploy to Staging
run: |
kubectl config set-cluster staging --server="${{ secrets.STAGING_K8S_SERVER }}"
kubectl config set-credentials admin --token="${{ secrets.STAGING_K8S_TOKEN }}"
kubectl apply -k k8s/staging/
kubectl rollout status deployment/myapp -n staging --timeout=5m
- name: Run E2E tests
run: npm run test:e2e -- --env=staging
- name: Run performance tests
run: |
k6 run --vus 10 --duration 30s tests/performance.js
- name: Check staging health
run: |
curl https://staging.example.com/health | jq '.status' | grep -q "healthy"
# Deploy to Production - Manual approval required
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v3
- name: Backup production database
run: |
kubectl exec -n production deployment/postgres -- \
pg_dump -U postgres app > backup-$(date +%Y%m%d-%H%M%S).sql
- name: Run database migrations
run: |
kubectl exec -n production deployment/postgres -- \
psql -U postgres -d app -f /migrations/migrate.sql
- name: Deploy to Production (Blue-Green)
run: |
kubectl config set-cluster prod --server="${{ secrets.PROD_K8S_SERVER }}"
kubectl config set-credentials admin --token="${{ secrets.PROD_K8S_TOKEN }}"
# Deploy green version
kubectl apply -k k8s/production/
kubectl rollout status deployment/myapp-green -n production --timeout=10m
# Switch traffic to green
kubectl patch service myapp -n production -p '{"spec":{"selector":{"version":"green"}}}'
- name: Monitor production metrics
run: |
sleep 300 # Wait 5 minutes
ERROR_RATE=$(curl -s prometheus.example.com/api/v1/query?query=rate5m)
if [ "$ERROR_RATE" -gt "0.01" ]; then
echo "Error rate too high, rolling back"
kubectl patch service myapp -n production -p '{"spec":{"selector":{"version":"blue"}}}'
exit 1
fi
- name: Notify team
if: success()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "✅ Production deployment successful!",
"version": "${{ github.sha }}",
"deployed_by": "${{ github.actor }}"
}
Part 7: Best Practices
Environment Management Checklist
✅ DO:
- Keep environments as similar as possible (especially staging and production)
- Use infrastructure as code (Terraform, Pulumi)
- Automate deployments to dev/staging
- Require manual approval for production
- Use environment-specific secrets management
- Run migrations before deploying application changes
- Monitor all environments (dev gets basic, prod gets comprehensive)
- Use feature flags for gradual rollouts
- Document environment differences
- Test disaster recovery in staging
❌ DON’T:
- Use production data in dev/staging (use anonymized data)
- Deploy untested code to production
- Skip staging environment to save costs
- Give everyone production access
- Hardcode environment-specific values
- Forget to test migrations in staging first
- Deploy on Friday afternoons (seriously)
- Use different tech stacks across environments
Conclusion
Multi-environment pipelines provide:
- Risk Mitigation: Test changes before production
- Fast Feedback: Issues caught in dev/staging, not production
- Confidence: Validated code progresses through gates
- Isolation: Each environment serves a specific purpose
- Reproducibility: Infrastructure as code ensures consistency
Key principles:
- Progressive promotion (dev → staging → prod)
- Environment parity (staging mirrors production)
- Automated testing at each stage
- Manual approval gates for production
- Environment-specific configuration management
The visual diagrams in this guide show how code progresses through environments, making the deployment pipeline transparent and safe.
Further Reading
- The Twelve-Factor App
- Kubernetes Kustomize
- Database Migrations Best Practices
- Environment Configuration Management
Build confidence through progressive promotion!