One of our clients was deploying their API once a week – on Saturdays at 3 AM, with two engineers on call and a rollback plan taped to someone’s monitor. After we implemented canary deployments with automated metric analysis, they moved to 15 deploys per day, during business hours, with zero incidents in six months.
The deployment strategy you choose determines whether shipping to production is a non-event or a fire drill. Here’s a practical breakdown of the strategies that matter, when to use each, and how to implement them in Kubernetes.
The Decision Matrix
| Strategy | Downtime | Risk | Complexity | Best For |
|---|---|---|---|---|
| Recreate | Yes | High | Low | Dev/staging environments |
| Rolling Update | No | Medium | Low | Most applications |
| Blue-Green | No | Low | Medium | Critical apps needing instant rollback |
| Canary | No | Very Low | High | High-traffic services with observable SLOs |
Rolling Updates
Rolling updates are Kubernetes’ default strategy, and for good reason – they work well for the vast majority of applications. Old pods are replaced with new ones incrementally, so the service never goes fully offline.
The two key tuning knobs are maxSurge and maxUnavailable. For a fast rollout, allow extra pods during the transition. For a risk-averse rollout, set maxSurge: 1 and maxUnavailable: 0 so you always maintain full capacity, at the cost of a slower rollout. Adding minReadySeconds forces each new pod to remain healthy for a set period before the rollout proceeds, giving you time to catch issues early.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 6
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2 # Allow up to 2 extra pods during the rollout
maxUnavailable: 1 # At most 1 pod can be down at any time
minReadySeconds: 30 # Pod must be healthy for 30s before proceeding
template:
spec:
containers:
- name: app
image: myapp:2.0.0
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
Operating rollouts day-to-day is straightforward with kubectl rollout. You can watch progress in real time, view history to understand what changed, pause mid-rollout if you spot errors in logs, and roll back to any previous revision with a single command. The instant rollback – kubectl rollout undo deployment/web-app – is what makes rolling updates practical even for teams without sophisticated monitoring.
Blue-Green Deployments
Blue-green gives you something rolling updates can’t: instant, atomic traffic cutover. You run two full environments – the live “blue” and the staged “green” – and switch between them by updating a Service selector.
# Blue deployment (currently serving production traffic)
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-blue
spec:
replicas: 5
selector:
matchLabels: { app: web-app, version: blue }
template:
metadata:
labels: { app: web-app, version: blue }
spec:
containers:
- name: app
image: myapp:1.0.0
---
# Green deployment (new version, staged and tested)
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-green
spec:
replicas: 5
selector:
matchLabels: { app: web-app, version: green }
template:
metadata:
labels: { app: web-app, version: green }
spec:
containers:
- name: app
image: myapp:2.0.0
---
# Service: the selector determines which deployment gets traffic
apiVersion: v1
kind: Service
metadata:
name: web-app
spec:
selector:
app: web-app
version: blue # Change to 'green' to cut over
ports:
- port: 80
targetPort: 8080
The cutover sequence is deliberate: deploy green, wait for all pods to pass readiness checks, smoke test green directly via kubectl port-forward, then patch the Service selector to point to green. If something breaks, rollback is a single kubectl patch command pointing the selector back to blue. Once stable, tear down the old deployment.
The trade-off is resource cost: blue-green requires double the compute during deployment. For most teams, that’s a reasonable price for sub-second rollback on critical services. In our experience, the confidence it gives teams – especially during business-hours deployments – pays for itself quickly.
Canary Deployments
Canary is the strategy for teams that deploy frequently to high-traffic services and need mathematical confidence that a release is safe. You route a small percentage of traffic to the new version, monitor key metrics, and gradually increase the percentage.
Manual canary works by running a small canary deployment alongside the stable one, with NGINX Ingress annotations controlling the traffic split. But manual canary doesn’t scale – someone has to watch dashboards and adjust weights by hand. For production use, we recommend Flagger, which automates the entire process:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: web-app
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
service:
port: 80
targetPort: 8080
analysis:
interval: 1m # Check metrics every minute
threshold: 5 # Roll back after 5 failed checks
maxWeight: 50 # Cap canary traffic at 50%
stepWeight: 10 # Increase by 10% each interval
metrics:
- name: request-success-rate
thresholdRange:
min: 99 # Abort if success rate drops below 99%
interval: 1m
- name: request-duration
thresholdRange:
max: 500 # Abort if p99 latency exceeds 500ms
interval: 1m
With this configuration, a deployment that causes error rate to spike or latency to degrade is automatically rolled back before it affects more than 50% of users. No human intervention required. The key design decision is choosing the right metrics and thresholds – too aggressive and you’ll get false rollbacks, too lenient and bad releases slip through.
GitOps With ArgoCD
For teams that want every deployment to be auditable, reproducible, and triggered by a Git commit, ArgoCD is the standard. You define an Application resource that points to a Git repository containing your Kubernetes manifests. ArgoCD watches the repo and syncs the cluster to match, with automated pruning of deleted resources and self-healing for manual drift.
ArgoCD also supports Argo Rollouts, which gives you blue-green and canary as first-class deployment primitives with features like manual promotion gates and configurable scale-down delays. The combination of GitOps workflow with progressive delivery gives you both auditability and safety – every deployment is a Git commit, and every promotion is metric-validated.
Health Checks Done Right
Every deployment strategy depends on accurate health checks. Get these wrong, and Kubernetes will route traffic to broken pods or kill healthy ones. The three probe types serve distinct purposes:
containers:
- name: app
image: myapp:2.0.0
# Readiness: "Can this pod handle requests?"
# Failing removes the pod from the Service endpoint
readinessProbe:
httpGet: { path: /readyz, port: 8080 }
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
# Liveness: "Is this pod fundamentally healthy?"
# Failing triggers a pod restart
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3
# Startup: "Has the app finished initializing?"
# Protects slow-starting apps from premature liveness kills
startupProbe:
httpGet: { path: /healthz, port: 8080 }
periodSeconds: 5
failureThreshold: 30
A common and dangerous mistake: making the liveness probe hit a dependency like a database or external API. If that dependency goes down, Kubernetes restarts all your pods – cascading the outage instead of isolating it. Liveness should check the process itself, not its dependencies. Use readiness probes for dependency checks so unhealthy pods stop receiving traffic without being killed.
Monitoring and Operational Best Practices
Set up Prometheus queries for three signals during every deployment: error rate (should stay below your SLO threshold), p95 latency (watch for regression), and pod restart count (any restart during a rollout is a red flag). These three metrics will catch the vast majority of deployment-related issues before they impact users.
Beyond monitoring, a few operational patterns make a significant difference. Label every resource with app, version, commit, and team – labels are how you query, filter, and debug in production. Always set resource requests and limits so a noisy pod can’t starve its neighbors. Use Pod Disruption Budgets to prevent cluster maintenance from taking down your service. And add a pre-stop hook with a short sleep to drain in-flight connections gracefully before pod termination.
Choosing Your Strategy
The decision is simpler than it seems:
- Rolling updates are the right default. Use them unless you have a specific reason not to.
- Blue-green when your team needs the confidence of instant rollback and can afford double the resources during deployment.
- Canary when you deploy frequently to high-traffic services and want automated, metric-driven promotion.
- GitOps when auditability and reproducibility matter – every deployment is a Git commit, every rollback is a revert.
Most teams should start with rolling updates, add health checks and monitoring, then graduate to canary when their observability stack is mature enough to support automated analysis. The strategy matters less than the fundamentals: good health checks, proper resource limits, and metrics you actually watch.