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.
What you'll learn
- ✓Understand blue-green, canary, and rolling deployment patterns
- ✓Configure each strategy in Kubernetes manifests
- ✓Choose the right strategy for your application
- ✓Implement automated rollbacks for failed deployments
Prerequisites
- •Basic Kubernetes knowledge
- •Familiarity with CI/CD pipeline concepts
Deploying new code to production is one of the highest-risk activities in software delivery. A bad deployment can take down your entire application, frustrate users, and cost your business real money. Deployment strategies exist to reduce that risk by controlling how new versions of your application reach users.
In this guide, we will walk through the three most widely used deployment strategies: blue-green, canary, and rolling deployments. You will learn how each one works, when to use it, and how to configure it in a real Kubernetes environment.
Why Deployment Strategies Matter
The simplest deployment approach is to stop the old version and start the new one. This “big bang” method has an obvious problem: downtime. During the switch, your application is unavailable. If the new version has a bug, every single user is affected immediately.
Modern deployment strategies solve these problems by introducing the new version gradually, keeping the old version available as a fallback, or both. The right strategy depends on your application’s architecture, your tolerance for risk, and how quickly you need to roll back if something goes wrong.
Blue-Green Deployments
Blue-green deployment maintains two identical production environments. One environment (blue) serves live traffic while the other (green) sits idle or runs the new version. When you are ready to deploy, you switch traffic from blue to green.
How It Works
- The blue environment runs the current production version.
- You deploy the new version to the green environment.
- You run smoke tests and health checks against green.
- You switch the load balancer or router to point at green.
- Green is now live. Blue becomes idle and serves as your rollback target.
Kubernetes Configuration
You can implement blue-green deployments in Kubernetes using two Deployments and a Service that switches between them:
# blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-blue
labels:
app: myapp
version: blue
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: myapp
image: myapp:1.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
# green-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-green
labels:
app: myapp
version: green
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: green
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:
- name: myapp
image: myapp:2.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
# service.yaml - switch by changing the version selector
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
version: blue # Change to "green" to switch traffic
ports:
- protocol: TCP
port: 80
targetPort: 8080
To switch traffic, update the Service selector:
kubectl patch service myapp-service \
-p '{"spec":{"selector":{"version":"green"}}}'
To roll back, switch back to blue:
kubectl patch service myapp-service \
-p '{"spec":{"selector":{"version":"blue"}}}'
When to Use Blue-Green
Blue-green deployments are ideal when you need instant rollback capability and can afford to run double the infrastructure. They work best for stateless applications where both environments can share the same database. Be cautious with database migrations since both versions must be compatible with the current schema during the switch.
Canary Deployments
Canary deployments release the new version to a small subset of users first. If metrics look good, you gradually increase the percentage of traffic going to the new version until it handles 100% of requests.
How It Works
- Deploy the new version alongside the current version.
- Route a small percentage (for example, 5%) of traffic to the new version.
- Monitor error rates, latency, and other key metrics.
- Gradually increase traffic to the new version (10%, 25%, 50%, 100%).
- If metrics degrade at any step, roll back by sending all traffic to the old version.
Kubernetes Configuration with Nginx Ingress
You can implement canary deployments using Nginx Ingress annotations:
# stable-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-stable
spec:
replicas: 3
selector:
matchLabels:
app: myapp
track: stable
template:
metadata:
labels:
app: myapp
track: stable
spec:
containers:
- name: myapp
image: myapp:1.0.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: myapp-stable
spec:
selector:
app: myapp
track: stable
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-stable
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-stable
port:
number: 80
# canary-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-canary
spec:
replicas: 1
selector:
matchLabels:
app: myapp
track: canary
template:
metadata:
labels:
app: myapp
track: canary
spec:
containers:
- name: myapp
image: myapp:2.0.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: myapp-canary
spec:
selector:
app: myapp
track: canary
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-canary
port:
number: 80
Increase canary weight gradually:
# Increase to 25%
kubectl annotate ingress myapp-canary \
nginx.ingress.kubernetes.io/canary-weight="25" --overwrite
# Increase to 50%
kubectl annotate ingress myapp-canary \
nginx.ingress.kubernetes.io/canary-weight="50" --overwrite
# Full rollout - promote canary to stable
kubectl set image deployment/myapp-stable myapp=myapp:2.0.0
kubectl delete deployment myapp-canary
kubectl delete service myapp-canary
kubectl delete ingress myapp-canary
When to Use Canary
Canary deployments are the best choice when you need to validate a release with real user traffic before committing to a full rollout. They require good observability since you need metrics to decide whether to proceed or roll back. Canary is especially useful for high-traffic applications where even a small bug can impact thousands of users.
Rolling Deployments
Rolling deployments replace instances of the old version with the new version one at a time. At any point during the rollout, some instances run the old version and others run the new version. This is the default deployment strategy in Kubernetes.
How It Works
- Kubernetes starts a new pod with the new version.
- Once the new pod passes its readiness probe, an old pod is terminated.
- This process repeats until all pods run the new version.
- The rollout speed is controlled by
maxSurgeandmaxUnavailablesettings.
Kubernetes Configuration
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # At most 1 extra pod during update
maxUnavailable: 0 # All existing pods stay available
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:2.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
Monitor and control the rollout:
# Watch the rollout progress
kubectl rollout status deployment/myapp
# Pause if something looks wrong
kubectl rollout pause deployment/myapp
# Resume the rollout
kubectl rollout resume deployment/myapp
# Roll back to the previous version
kubectl rollout undo deployment/myapp
# Roll back to a specific revision
kubectl rollout undo deployment/myapp --to-revision=3
When to Use Rolling
Rolling deployments are the simplest strategy and work well for most applications. They do not require double the infrastructure like blue-green and they are built into Kubernetes natively. The main drawback is that during the rollout, both old and new versions serve traffic simultaneously, so your application must handle version compatibility. Rollback is also slower since Kubernetes needs to perform another rolling update in reverse.
Comparing the Strategies
Here is a quick comparison to help you decide:
| Factor | Blue-Green | Canary | Rolling |
|---|---|---|---|
| Rollback speed | Instant | Fast | Slow |
| Infrastructure cost | 2x during deploy | Slightly more | Minimal extra |
| Complexity | Medium | High | Low |
| Traffic control | All or nothing | Fine-grained | Automatic |
| Risk exposure | Full switch | Gradual | Gradual |
| Zero downtime | Yes | Yes | Yes |
Automating Rollbacks in Your Pipeline
Regardless of which strategy you choose, your CI/CD pipeline should include automated rollback triggers. Here is an example GitHub Actions workflow that checks deployment health and rolls back if needed:
# .github/workflows/deploy.yml
name: Deploy with Health Check
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/myapp \
myapp=myapp:${{ github.sha }}
- name: Wait for rollout
run: |
kubectl rollout status deployment/myapp --timeout=300s
- name: Health check
id: health
run: |
for i in $(seq 1 10); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://myapp.example.com/health)
if [ "$STATUS" != "200" ]; then
echo "Health check failed with status $STATUS"
echo "healthy=false" >> "$GITHUB_OUTPUT"
exit 0
fi
sleep 5
done
echo "healthy=true" >> "$GITHUB_OUTPUT"
- name: Rollback on failure
if: steps.health.outputs.healthy == 'false'
run: |
echo "Rolling back deployment..."
kubectl rollout undo deployment/myapp
kubectl rollout status deployment/myapp --timeout=300s
exit 1
Wrapping Up
Blue-green deployments give you instant rollback by maintaining two full environments. Canary deployments let you test with real traffic before committing to a full release. Rolling deployments are the simplest option and work well for most teams starting out.
Start with rolling deployments if you are new to Kubernetes. Move to canary when you have good observability in place and need more control over rollouts. Use blue-green when instant rollback is a hard requirement. Whichever strategy you choose, always include health checks and automated rollback in your pipeline.
Related articles
- CI/CD Blue-Green vs Canary Deployments Explained
Compare blue-green and canary deployment strategies, including how they handle rollback, traffic shifting, and observability, with concrete Kubernetes and AWS examples.
- DevOps 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.
- CI/CD Automated Deployment Rollbacks with Observability
Build automated rollback systems that use health checks, error rate thresholds, and observability signals to detect failures and revert deployments without human intervention.
- CI/CD Canary Deployments with Flagger Tutorial
Learn how to ship canary releases on Kubernetes using Flagger. Covers the control loop, metric analysis, traffic shifting, and how to roll back automatically when a release misbehaves.