Multi-Stage Deployment Patterns
Why Multi-Stage?
Multi-stage pipelines separate your CI/CD process into logical phases with gates between them. This gives you:
- Controlled promotion through environments (Dev → Test → Staging → Prod)
- Approval gates before production deployments
- Independent failure isolation per stage
- Audit trail of what was deployed and when
Pipeline Structure
trigger:
- main
stages:
- stage: Build
jobs:
- job: BuildAndTest
pool:
vmImage: 'ubuntu-latest'
steps:
- script: npm ci && npm run build && npm test
- publish: $(System.DefaultWorkingDirectory)/dist
artifact: app
- stage: DeployDev
dependsOn: Build
jobs:
- deployment: DeployToDev
environment: 'development'
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- task: AzureCLI@2
inputs:
azureSubscription: 'dev-connection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az webapp deploy --resource-group rg-dev --name app-dev \
--src-path $(Pipeline.Workspace)/app/dist.zip
- stage: DeployProd
dependsOn: DeployDev
condition: succeeded()
jobs:
- deployment: DeployToProd
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- task: AzureCLI@2
inputs:
azureSubscription: 'prod-connection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az webapp deploy --resource-group rg-prod --name app-prod \
--src-path $(Pipeline.Workspace)/app/dist.zip
Environments and Approvals
Define environments in Azure DevOps with:
- Approval checks — Require one or more approvers before deployment
- Business hours — Only allow deployments during specified windows
- Exclusive lock — Prevent concurrent deployments to the same environment
- Branch control — Only allow deployments from specific branches
Deployment Strategies
RunOnce
The simplest strategy — deploy once and complete.
Rolling
Deploy to a subset of targets at a time:
strategy:
rolling:
maxParallel: 2
deploy:
steps:
- script: ./deploy.sh
Canary
Deploy to a small percentage first, then expand:
strategy:
canary:
increments: [10, 50]
deploy:
steps:
- script: ./deploy.sh
Rollback
Use the on: failure lifecycle hook:
strategy:
runOnce:
deploy:
steps:
- script: ./deploy.sh
on:
failure:
steps:
- script: ./rollback.sh
Best Practices
- Use deployment jobs (not regular jobs) for environment tracking
- Require approvals on production environments
- Download artifacts rather than rebuilding in each stage
- Use variable groups scoped per environment
- Implement health checks after deployment before marking as successful