Rollbacks in Azure Pipelines
A deployment that cannot be rolled back is a liability. Every production deployment should have a defined, tested rollback path before the deployment begins — not improvised during an incident. Azure provides service-specific rollback mechanisms for App Service, Container Apps, and AKS, and Azure Pipelines can orchestrate them through a dedicated rollback stage with human approval gates.
Rollback strategies per service
The rollback mechanism depends on the Azure service being used. Each service has a different approach, and the speed and reliability of rollback varies:
| Service | Rollback mechanism | Rollback time | Data risk |
|---|---|---|---|
| App Service (slots) | Swap staging and production slots back | Seconds | Low (app only, not DB) |
| Container Apps | Shift traffic back to previous revision | Seconds | Low |
| AKS | kubectl rollout undo or reapply previous image | 1–3 minutes | Low |
| Terraform (infra) | Reapply previous Terraform code version | Minutes to hours | Medium (depends on resources) |
| Database migrations | Manual reversal or restore from backup | Minutes to hours | High — data may be lost |
App Service: slot swap rollback
When you deploy using slot swap (blue-green pattern), rollback is another slot swap in the opposite direction. The previous production version is still running in the staging slot, fully warmed up, ready to serve traffic. The swap-back takes seconds.
# Rollback: swap staging (old version) back to production
az webapp deployment slot swap \
--name app-myapp-prod \
--resource-group rg-prod \
--slot staging \
--target-slot production
# Verify rollback by checking the currently deployed commit or image
az webapp show \
--name app-myapp-prod \
--resource-group rg-prod \
--query "siteConfig.linuxFxVersion"If you did not use slots (deployed directly to production), rollback requires redeploying the previous artifact. This is slower and less reliable than slot-based rollback. This is one of the primary reasons to use deployment slots for all production deployments — not just for blue-green promotion, but for rollback capability.
Container Apps: revision traffic shift
Container Apps revisions are immutable — the previous version is still deployed and can receive traffic again with a single CLI command:
# List revisions to find the previous stable revision name
az containerapp revision list \
--name myapp \
--resource-group rg-prod \
--query "[].{name:name, active:properties.active, traffic:properties.trafficWeight, created:properties.createdTime}" \
--output table
# Rollback: shift all traffic back to the previous revision
az containerapp ingress traffic set \
--name myapp \
--resource-group rg-prod \
--revision-weight myapp--v1-stable=100
# Deactivate the failed revision to stop it consuming resources
az containerapp revision deactivate \
--name myapp \
--resource-group rg-prod \
--revision myapp--v2-failedAKS: rollout undo and image revert
Kubernetes tracks the rollout history of Deployments. The kubectl rollout undo command reverts to the previous deployment revision, re-deploying the previous container image:
# Check rollout history to see available revisions
kubectl rollout history deployment/myapp -n production
# Rollback to the previous revision
kubectl rollout undo deployment/myapp -n production
# Rollback to a specific historical revision
kubectl rollout undo deployment/myapp -n production --to-revision=3
# Monitor the rollback progress
kubectl rollout status deployment/myapp -n production
# Verify the rolled-back image
kubectl get deployment myapp -n production \
-o jsonpath='{.spec.template.spec.containers[0].image}'By default, Kubernetes keeps 10 rollout history revisions. Configure spec.revisionHistoryLimit in your Deployment manifest if you need more or fewer historical revisions available for rollback.
Terraform: reverting infrastructure changes
Infrastructure rollback with Terraform is more complex than application rollback. Terraform applies the difference between the desired state (in code) and the current state (in the state file). To roll back, revert the Terraform code to the previous version and run terraform apply.
# Revert to the previous Terraform code version
git checkout HEAD~1 -- terraform/
# Review what will change before applying
terraform plan \
-var-file=production.tfvars
# Apply the rollback (restores previous infrastructure state)
terraform apply \
-var-file=production.tfvars \
-auto-approveSome Terraform resources cannot be rolled back cleanly. Deleting a database (because the new config removed it) loses data. Replacing a VM (because the image changed) may have a new IP. Before applying any Terraform rollback, run terraform plan and carefully review every resource marked for destruction or replacement.
Never roll back a Terraform state file manually to a previous backup as a rollback strategy. State files represent the actual state of deployed resources. If you restore an old state file, Terraform will believe resources that actually exist do not exist, and may try to recreate or destroy them on the next apply. Roll back the code, not the state file.
Rollback stage in Azure Pipelines
Build the rollback path into the pipeline rather than improvising it during an incident. A dedicated rollback stage, gated by a manual approval, makes rollback a controlled, documented operation:
stages:
- stage: Build
jobs:
- job: BuildApp
steps:
- task: DotNetCoreCLI@2
inputs:
command: publish
arguments: '--output $(Build.ArtifactStagingDirectory)'
- publish: $(Build.ArtifactStagingDirectory)
artifact: drop
- stage: DeployProd
dependsOn: DeployStaging
jobs:
- deployment: Deploy
environment: env-prod
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
inputs:
azureSubscription: 'sc-myapp-prod'
appName: 'app-myapp-prod'
deployToSlotOrASE: true
slotName: staging
package: '$(Pipeline.Workspace)/drop/**/*.zip'
- task: AzureAppServiceManage@0
displayName: 'Swap staging to production'
inputs:
azureSubscription: 'sc-myapp-prod'
action: 'Swap Slots'
webAppName: 'app-myapp-prod'
sourceSlot: staging
- stage: Rollback
dependsOn: DeployProd
# This stage only runs when triggered manually
condition: false
jobs:
- deployment: RollbackProd
environment: env-prod # Manual approval gate on this environment
strategy:
runOnce:
deploy:
steps:
- task: AzureAppServiceManage@0
displayName: 'Swap back: restore previous production version'
inputs:
azureSubscription: 'sc-myapp-prod'
action: 'Swap Slots'
webAppName: 'app-myapp-prod'
sourceSlot: stagingThe rollback stage has condition: false so it never runs automatically. To trigger a rollback, navigate to the pipeline run in Azure DevOps, select “Stages,” and manually run the Rollback stage. The approval gate on env-prod requires a designated approver to sign off before the swap-back executes.
When you cannot roll back: database migrations
Database schema migrations are the most common reason a rollback becomes impossible or dangerous. If the new version runs a migration that drops a column, renames a table, or changes a data type in a way the old version cannot handle, rolling back the application code leaves the database in a state incompatible with the old code.
The only safe approach is to design migrations to be rollback-compatible:
Expand-contract pattern. Add the new column or table (expand phase), deploy the new application version that writes to both old and new schema, validate, then remove the old column or table (contract phase) in a separate, later deployment. The contract phase is only executed after the old application version is fully retired.
Never drop until you must. Adding columns or tables is safe and backwards-compatible. Removing columns removes data. Never remove a column in the same deployment as adding the new version that does not use it.
Feature flags for data model changes. If a new feature requires a schema change, deploy the schema change with the feature behind a flag. The old application version does not use the new column. The flag controls when the new code path (and the new column) are activated. Rollback is now just turning the flag off, not touching the schema.
Common mistakes
- Not testing the rollback procedure before a production deployment. The first time you execute a rollback should not be during a production incident. Test rollback in staging — deploy a new version, confirm the rollback path works, then restore. This also validates that the previous artifact is still available and that the rollback CLI commands work with the current infrastructure configuration.
- Waiting too long to rollback hoping the problem will resolve itself. If your monitoring detects a spike in error rates after a deployment, the rollback decision should be made within minutes, not hours. The longer the degraded version runs, the more customers are affected and the more pressure builds. Define the rollback trigger criteria before the deployment: “if error rate exceeds 1% within 15 minutes of deployment, roll back immediately.”
- Redeploying the old artifact instead of using slot swap or revision traffic shift. Redeploying from an artifact takes minutes and risks introducing new problems (artifact not found, deployment timeout, configuration mismatch). A slot swap or revision traffic shift takes seconds and simply changes which already-running version serves traffic. The old version must stay warm and available precisely for this reason — always prefer traffic switching over redeployment for rollback.
Summary
- App Service slot swap rollback is a swap-back of the staging and production slots — the old version stays warm in the staging slot and rollback takes seconds.
- Container Apps rollback shifts traffic back to a previous revision with a single
az containerapp ingress traffic setcommand. - AKS rollback uses
kubectl rollout undoto revert to the previous Deployment revision and container image. - Build a Rollback stage in the pipeline YAML with a manual approval gate — tested and ready before deployment, not improvised during an incident.
- Backwards-incompatible database migrations make application rollback impossible. Use the expand-contract pattern and never drop columns in the same deployment as the application change that removes usage of them.
Frequently asked questions
What is the difference between a rollback and a revert?
A rollback restores a previously running version of an application or infrastructure — often by redeploying a known-good artifact or switching traffic back to a previous slot. A revert typically refers to undoing a Git commit and creating a new commit that removes the change. In the context of deployments, rollback is the operational action (restore the service), and revert is the code action (remove the change from version control). In a crisis, prioritise the rollback first, then the revert.
When can I not roll back?
You cannot safely roll back when the new version has run a backwards-incompatible database migration. Reverting the application code leaves the database in the new schema, which the old application code cannot understand. This is why forwards-only migrations (additive changes, no column removal until the old version is fully retired) are essential. You also cannot roll back cleanly if the new version has written data in a format incompatible with the old version.
Should I create a dedicated rollback pipeline or rerun the previous pipeline?
Both approaches work. A dedicated rollback stage or pipeline is explicit and easier to trigger under pressure — one button, defined in advance, tested. Rerunning the previous successful pipeline run with the same artifact achieves the same result but requires navigating the Azure DevOps UI to find the right run. For services where rollback speed matters, a dedicated rollback stage with a manual approval gate is more reliable in a stressful incident.