Zero-Downtime Deployments: Strategies and Patterns
Master zero-downtime deployment strategies including rolling updates, blue-green, canary, and feature flags. Learn practical patterns for Kubernetes and cloud platforms.
What you'll learn
- ✓Why traditional deployments cause downtime and how to avoid it
- ✓Rolling update, blue-green, and canary deployment strategies
- ✓Implementing each strategy in Kubernetes with working examples
- ✓Database migration patterns that prevent deployment failures
Prerequisites
- •Basic Kubernetes knowledge (Deployments, Services)
- •Experience deploying web applications
- •Understanding of load balancers and health checks
Why Deployments Cause Downtime
A naive deployment process goes like this: stop the old version, start the new version. During the gap between stopping and starting, your service is unavailable. Even if the gap is only a few seconds, that translates to failed requests, broken user sessions, and lost revenue.
Zero-downtime deployment means releasing new versions without any interruption to users. Every request is served, old or new, with no errors during the transition. Achieving this requires understanding several strategies and choosing the right one for your situation.
Prerequisites for Zero-Downtime
Before diving into strategies, your application must meet certain requirements:
Health checks: Your application must expose a health endpoint that returns the true readiness state. Load balancers and orchestrators use this to know when a new instance is ready to receive traffic.
# Kubernetes readiness and liveness probes
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
Graceful shutdown: When an instance receives a termination signal, it must finish in-flight requests before exiting rather than dropping them immediately.
# Kubernetes lifecycle hook for graceful shutdown
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
terminationGracePeriodSeconds: 30
Backward compatibility: The new version must work alongside the old version during the transition period. This means APIs must be backward-compatible and database schemas must support both versions simultaneously.
Strategy 1: Rolling Updates
Rolling updates gradually replace old instances with new ones. At any point during the deployment, some instances run the old version and some run the new version.
How It Works
- Start a new pod with the updated version.
- Wait for it to pass health checks.
- Route traffic to the new pod.
- Terminate one old pod.
- Repeat until all pods are running the new version.
Kubernetes Implementation
Rolling updates are the default strategy in Kubernetes:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: production
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myregistry/api:v2.1.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Key settings:
maxSurge: 1allows one extra pod during the update, so capacity goes from 5 to 6 temporarily.maxUnavailable: 0ensures all 5 pods remain available throughout. No pod is removed until a new one is ready.
Deploy and monitor:
# Trigger the rolling update
kubectl set image deployment/api api=myregistry/api:v2.2.0 -n production
# Watch the rollout progress
kubectl rollout status deployment/api -n production
# If something goes wrong, roll back
kubectl rollout undo deployment/api -n production
Pros and Cons
Rolling updates are simple and resource-efficient because you only need one extra pod at a time. However, during the rollout, users may be served by either the old or new version, which can cause issues if the versions behave differently. Rollback is also slow because it requires another full rolling update in reverse.
Strategy 2: Blue-Green Deployment
Blue-green deployment runs two identical production environments. The “blue” environment runs the current version, and the “green” environment runs the new version. Once green is verified, you switch traffic from blue to green instantly.
How It Works
- Blue is live, serving all traffic.
- Deploy the new version to green.
- Run smoke tests and validation against green.
- Switch the load balancer or service to point to green.
- Green is now live. Blue remains as a rollback target.
Kubernetes Implementation
Use two Deployments and switch the Service selector:
# Blue deployment (current version)
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-blue
namespace: production
spec:
replicas: 5
selector:
matchLabels:
app: api
version: blue
template:
metadata:
labels:
app: api
version: blue
spec:
containers:
- name: api
image: myregistry/api:v2.1.0
ports:
- containerPort: 8080
---
# Green deployment (new version)
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-green
namespace: production
spec:
replicas: 5
selector:
matchLabels:
app: api
version: green
template:
metadata:
labels:
app: api
version: green
spec:
containers:
- name: api
image: myregistry/api:v2.2.0
ports:
- containerPort: 8080
The Service initially points to blue:
apiVersion: v1
kind: Service
metadata:
name: api
namespace: production
spec:
selector:
app: api
version: blue # Change to "green" to switch
ports:
- port: 80
targetPort: 8080
Switch traffic by updating the selector:
# Verify green is healthy
kubectl get pods -l version=green -n production
# Switch traffic to green
kubectl patch service api -n production \
-p '{"spec":{"selector":{"version":"green"}}}'
# If something goes wrong, switch back to blue
kubectl patch service api -n production \
-p '{"spec":{"selector":{"version":"blue"}}}'
Pros and Cons
Blue-green deployments provide instant rollback (just switch the selector back) and eliminate the mixed-version problem of rolling updates. The downside is cost: you need double the resources during deployment. For large services, this can be significant.
Strategy 3: Canary Deployment
Canary deployment routes a small percentage of traffic to the new version first. If metrics look good, you gradually increase the percentage until the new version handles all traffic.
How It Works
- Deploy the new version alongside the current version.
- Route 5% of traffic to the new version.
- Monitor error rates, latency, and business metrics.
- If healthy, increase to 25%, 50%, and finally 100%.
- If problems appear at any stage, route all traffic back to the old version.
Kubernetes with Nginx Ingress
Use weighted routing with Nginx Ingress:
# Stable deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-stable
spec:
replicas: 5
selector:
matchLabels:
app: api
track: stable
template:
metadata:
labels:
app: api
track: stable
spec:
containers:
- name: api
image: myregistry/api:v2.1.0
---
# Canary deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-canary
spec:
replicas: 1
selector:
matchLabels:
app: api
track: canary
template:
metadata:
labels:
app: api
track: canary
spec:
containers:
- name: api
image: myregistry/api:v2.2.0
# Stable ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-stable
annotations:
nginx.ingress.kubernetes.io/canary: "false"
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-stable
port:
number: 80
---
# Canary ingress with weight
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-canary
port:
number: 80
Increase the canary weight gradually:
# Increase canary to 25%
kubectl annotate ingress api-canary \
nginx.ingress.kubernetes.io/canary-weight="25" --overwrite -n production
# Increase to 50%
kubectl annotate ingress api-canary \
nginx.ingress.kubernetes.io/canary-weight="50" --overwrite -n production
# Full rollout: remove canary, update stable
kubectl annotate ingress api-canary \
nginx.ingress.kubernetes.io/canary-weight="100" --overwrite -n production
Automated Canary with Argo Rollouts
For automated canary analysis, use Argo Rollouts:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api
spec:
replicas: 5
strategy:
canary:
steps:
- setWeight: 5
- pause: { duration: 5m }
- setWeight: 25
- pause: { duration: 10m }
- setWeight: 50
- pause: { duration: 10m }
- setWeight: 100
analysis:
templates:
- templateName: success-rate
startingStep: 1
canaryService: api-canary
stableService: api-stable
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myregistry/api:v2.2.0
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
metrics:
- name: success-rate
interval: 60s
successCondition: result[0] >= 0.99
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(http_requests_total{app="api",status!~"5.."}[5m]))
/
sum(rate(http_requests_total{app="api"}[5m]))
Argo Rollouts automatically promotes or rolls back the canary based on the analysis template. If the success rate drops below 99%, the rollout is aborted and all traffic returns to the stable version.
Database Migration Patterns
Database changes are the hardest part of zero-downtime deployments. You cannot simply add a required column or rename a table while both versions of your application are running.
The Expand-Contract Pattern
Break schema changes into backward-compatible steps:
Step 1: Expand - Add the new column as nullable or with a default value.
-- Deploy 1: Add column (backward compatible)
ALTER TABLE orders ADD COLUMN shipping_method VARCHAR(50) DEFAULT 'standard';
Step 2: Migrate - Backfill existing data and update the application to write to both old and new columns.
-- Deploy 2: Backfill data
UPDATE orders SET shipping_method = 'standard' WHERE shipping_method IS NULL;
Step 3: Contract - Once all application instances use the new column, remove the old one.
-- Deploy 3: Drop old column (only after all instances are updated)
ALTER TABLE orders DROP COLUMN legacy_shipping_type;
Rules for Safe Migrations
- Never rename a column in a single step. Add the new column, migrate data, update the app, then drop the old column.
- Never add a NOT NULL constraint without a default value.
- Never drop a column that the current running version still reads from.
- Always test migrations against a copy of production data before applying them.
- Use migration tools that support reversible migrations (Flyway, Alembic, Rails Migrations).
Choosing the Right Strategy
| Strategy | Rollback Speed | Resource Cost | Complexity | Best For |
|---|---|---|---|---|
| Rolling Update | Slow (minutes) | Low (+1 pod) | Low | Standard deployments, stateless services |
| Blue-Green | Instant | High (2x) | Medium | Critical services requiring instant rollback |
| Canary | Fast (seconds) | Medium | High | High-traffic services, risky changes |
For most teams, rolling updates are the starting point. Graduate to canary deployments for critical services where you want to validate changes with real traffic before full rollout. Use blue-green when you need the ability to switch back instantly, such as major version upgrades.
Wrapping Up
Zero-downtime deployments are not a luxury; they are an expectation for modern applications. Start with rolling updates and proper health checks, which Kubernetes provides out of the box. Add graceful shutdown handling and backward-compatible database migrations to eliminate the most common sources of deployment-related downtime. As your deployment confidence grows, adopt canary deployments with automated analysis to catch issues before they reach all users. The investment in zero-downtime patterns pays dividends in reliability, developer confidence, and the ability to deploy more frequently with less risk.
Related articles
- DevOps Blue-Green vs Canary Deployments Explained
Compare blue-green and canary deployment strategies with practical examples to choose the right zero-downtime release approach for your team.
- CI/CD CI/CD Deployment Strategies: Blue-Green, Canary, Rolling
Learn the most popular CI/CD deployment strategies including blue-green, canary, and rolling deployments with practical examples and configuration.
- DevOps Container Orchestration: Docker Swarm vs Kubernetes
Compare Docker Swarm and Kubernetes for container orchestration. Learn the architecture, setup, scaling, and networking of both platforms with practical examples.
- DevOps GitOps with ArgoCD: Deploy Kubernetes Apps Automatically
Learn GitOps principles and deploy Kubernetes applications with ArgoCD. Covers installation, app configuration, sync policies, multi-environment setups, and rollbacks.