When Rolling Updates Go Wrong

It was 2 AM on a Tuesday when our rolling update started killing pods faster than they could handle graceful shutdowns. We watched in horror as connection errors spiked across our entire payment processing pipeline. The deployment had passed all our tests, but production traffic revealed a race condition we’d never considered: our application needed 45 seconds to properly flush in-memory transactions, but our terminationGracePeriodSeconds was set to the default 30.

That night taught me that Kubernetes deployment strategies aren’t just about moving code from one version to another. They’re about understanding the complex dance between your application’s lifecycle, infrastructure constraints, and business requirements. The difference between a smooth deployment and a production incident often comes down to details that seem trivial in staging environments.

Blue-Green: The Safety Net with Hidden Costs

After that rolling update disaster, we switched to blue-green deployments for our core services. The concept is elegantly simple: maintain two identical production environments, route all traffic to one while preparing the other, then flip the switch. When something goes wrong, you flip back. We implemented this using Kubernetes services with label selectors, maintaining blue and green ReplicaSets simultaneously.

The psychological comfort was immediate. Deployments became a single atomic operation rather than a gradual process where problems could compound. But blue-green came with costs we hadn’t anticipated. Our AWS bill doubled overnight as we maintained duplicate infrastructure. More critically, our stateful services like Redis and PostgreSQL became bottlenecks. They couldn’t be easily duplicated, so we ended up with a hybrid approach where only stateless services got true blue-green treatment.

The lesson here isn’t that blue-green is wrong, but that your deployment strategy must align with your infrastructure reality. If you’re running on expensive cloud resources or have significant stateful components, the economics might not work. We eventually moved to blue-green for critical user-facing services only, keeping rolling updates for internal APIs where brief downtime was acceptable.

Canary Releases: Monitoring as a First-Class Citizen

Canary deployments felt like the natural evolution of our deployment maturity. Instead of the binary choice of blue-green, we could gradually shift traffic percentages while monitoring key metrics. Our first implementation used Istio’s traffic splitting capabilities, routing 5% of traffic to the new version while watching error rates, response times, and business metrics like successful payment processing.

The devil, as always, was in the monitoring details. We discovered that 5% traffic wasn’t enough to catch edge cases that occurred in roughly 1 in 200 requests. Our sample size was too small to detect problems before they affected the majority user base. We had to develop more sophisticated metrics: not just error rates, but error rate deltas compared to the stable version, and confidence intervals around those measurements.

The automation became crucial. Manual canary deployments are a recipe for human error at 3 AM. We built Kubernetes operators that could automatically promote or rollback based on SLI thresholds. A 2x increase in 95th percentile latency would trigger an automatic rollback within 5 minutes. This required investing heavily in observability infrastructure, but the confidence it gave us was worth every engineering hour spent on metrics collection and alerting.

Rolling Updates Revisited: Getting the Details Right

We eventually came back to rolling updates for many services, but with hard-won wisdom about the configuration details that matter. The maxUnavailable and maxSurge parameters aren’t just numbers you copy from tutorials. They define the fundamental tradeoff between deployment speed and resource usage during transitions.

Our API services now use maxUnavailable: 0 and maxSurge: 50%, ensuring we never lose capacity during deployments but accepting that we’ll temporarily use 150% of our normal pod count. For background job processors, we flip this: maxUnavailable: 50% and maxSurge: 0, prioritizing resource efficiency over maintaining constant capacity. The key insight is that these parameters should reflect your service’s specific requirements, not generic best practices.

Readiness and liveness probes became our deployment guardrails. A properly configured readiness probe prevents new pods from receiving traffic before they’re truly ready, while liveness probes catch the zombie processes that pass readiness checks but can’t actually serve requests. We learned to make our readiness probes application-specific: for our payment service, a pod isn’t ready until it has successfully connected to the payment gateway and validated its API credentials.

The Operational Reality Check

After three years of production Kubernetes deployments, I’ve learned that the best deployment strategy is the one your team can operate confidently at 3 AM. Sophisticated canary deployments mean nothing if your on-call engineer doesn’t understand how to interpret the metrics or manually intervene when automation fails.

We standardized on a hybrid approach: blue-green for our customer-facing web application where any downtime is immediately visible, canary releases for our API services where we have good metrics and can detect problems quickly, and rolling updates for internal services where brief disruptions are acceptable. Each strategy requires different operational expertise and monitoring infrastructure.

The most important lesson is that deployment strategies are not set-and-forget decisions. As your applications evolve, your traffic patterns change, and your team grows, your deployment approach should evolve too. What deployment strategies has your team adopted, and more importantly, what harsh realities have shaped those choices?