If your deployment process involves a maintenance page, a 2 AM window, or a Slack message saying "don't push to main for the next hour" — your deployment process is broken.
Zero-downtime deployment isn't a luxury. It's a requirement for any production system that users rely on. And it's simpler to achieve than most teams think.
This guide covers the three main strategies, when to use each, and the hardest part that nobody talks about — database migrations.
The Three Deployment Strategies
Every zero-downtime deployment uses one of these three patterns. Each has different trade-offs in complexity, cost, and risk.
1. Blue-Green Deployment
The simplest and most reliable strategy. Two identical environments. One serves traffic (Blue), one sits idle (Green).
- Deploy the new version to Green
- Run automated health checks and smoke tests against Green
- Switch the load balancer to point to Green
- Green is now live. Blue is the instant rollback target.
- After a safety period (15–30 minutes), tear down Blue or keep it for the next deployment
Rollback time: Under 30 seconds (just switch the load balancer back)
Best for: Most applications. Especially good when you need guaranteed instant rollback. Our default recommendation.
Trade-off: Requires double the infrastructure during deployment (but only for 15–30 minutes). With containers on Fargate, the extra cost is negligible.
"We've done over 200 blue-green deployments for clients. Zero have caused user-facing downtime. The strategy is boring — and that's exactly why it works."
2. Canary Deployment
Deploy the new version to a small percentage of your fleet first. Monitor closely. If everything looks good, gradually increase to 100%.
- Deploy the new version to 5% of instances/containers
- Monitor error rates, latency, and business metrics for 10–15 minutes
- If metrics are healthy, increase to 25%, then 50%, then 100%
- If anything looks wrong at any stage, roll back the canary instances immediately
Rollback time: 1–2 minutes (terminate canary instances, traffic returns to old version)
Best for: High-traffic applications where a bug in the new version would affect millions of users. The blast radius of a canary failure is limited to 5% of traffic.
Trade-off: More complex to set up. Requires good monitoring to detect issues in the canary. You need to handle running two versions simultaneously (API compatibility).
3. Rolling Update
Replace instances one at a time (or in small batches). The simplest to configure but the least controllable.
- Take one instance out of the load balancer
- Update it with the new version
- Run health checks. If it passes, put it back in the load balancer.
- Repeat for the next instance until all instances are updated
Rollback time: Slow — you have to roll forward or reverse the entire process
Best for: Non-critical services, internal tools, or when infrastructure cost is a hard constraint
Trade-off: During the rollout, users are hitting both old and new versions simultaneously. Rollback is slow and messy. Not recommended for critical user-facing services.
Which Strategy Should You Use?
Small team, simple app, need fast rollback → Blue-Green. Start here. It covers 90% of use cases.
High-traffic, millions of users, risk-sensitive → Canary. Limit blast radius to a small percentage of traffic.
Internal tools, low-risk services → Rolling Update. Simple and cost-effective.
Not sure? → Blue-Green. You can always add canary later.
The Hard Part: Zero-Downtime Database Migrations
Here's the truth nobody mentions in deployment tutorials: the application deployment is the easy part. The database migration is where things break.
You can't just run ALTER TABLE on a production database during a deployment. Long-running schema changes lock tables. Dropped columns crash the old version. Renamed fields break both versions.
The solution is the expand-contract pattern:
Phase 1: Expand
- Add new columns or tables. Never remove or rename existing ones.
- Make new columns nullable or set sensible defaults
- Deploy application version that writes to BOTH old and new columns
- Backfill existing data from old columns to new columns
Phase 2: Migrate
- Deploy application version that reads from new columns only
- Verify everything works correctly for a full deployment cycle
Phase 3: Contract
- Remove old columns that are no longer used
- This is a separate deployment, days or weeks later
Yes, it's three deployments instead of one. But each one is safe, reversible, and zero-downtime. The alternative — a single risky migration — is how data gets lost.
"Every production data loss incident we've investigated had the same root cause: a schema change and an application change deployed simultaneously without a rollback plan."
Health Checks: The Deployment Safety Net
Your deployment strategy is only as good as your health checks. If the load balancer can't tell whether the new version is healthy, it can't protect your users.
Three levels of health checks:
- Liveness check: Is the process running? (TCP connection succeeds) — prevents routing to crashed containers
- Readiness check: Is the application ready to serve traffic? (HTTP 200 from
/health) — prevents routing to instances still starting up - Deep health check: Can the application actually do useful work? (Database connected, cache reachable, external APIs responding) — catches "running but broken" scenarios
Configure your load balancer to use the readiness check for routing decisions. Use the deep health check for your deployment pipeline's go/no-go decision.
Connection Draining: Don't Drop In-Flight Requests
When you take an instance out of a load balancer, active requests are still being processed. If you terminate the instance immediately, those requests fail.
Connection draining (also called deregistration delay) tells the load balancer to stop sending NEW requests to the instance but wait for existing requests to complete before removing it.
- Set deregistration delay to 30–60 seconds for most APIs
- For WebSocket connections, set it to match your maximum expected session length
- For long-running requests (file uploads, report generation), handle graceful shutdown in your application — listen for SIGTERM and finish in-progress work
Rollback: Plan for Failure
Every deployment should have a rollback plan that can execute in under 5 minutes. If you can't roll back quickly, you shouldn't be deploying.
Rollback checklist:
- Application rollback: Keep the previous container image tagged and ready. Rollback = deploy the previous tag.
- Database rollback: If you followed the expand-contract pattern, the old schema is still intact. If not, you need a tested database restore procedure.
- Feature flags: For high-risk features, deploy the code but hide it behind a feature flag. "Rolling back" is just toggling the flag off — no redeployment needed.
- Automated rollback: Configure your deployment tool to automatically roll back if error rates spike above a threshold within 5 minutes of deployment.
Implementing Zero-Downtime on AWS ECS
Here's the exact setup we use for most clients on AWS:
- ECS Service with deployment type set to
CODE_DEPLOY(blue-green) - Application Load Balancer with two target groups (blue and green)
- CodeDeploy application configured with:
- Traffic shifting: AllAtOnce (for most services) or Linear10PercentEvery1Minute (for canary-style)
- Automatic rollback on CloudWatch alarm trigger
- Original revision kept for 60 minutes as rollback target
- CloudWatch Alarms on HTTP 5xx rate — if it exceeds 1%, CodeDeploy auto-rolls back
- GitHub Actions triggers the deployment via
aws ecs update-service
Total setup time: half a day for someone who's done it before. A week if you're learning as you go.
Monitoring Your Deployments
After every deployment, watch these metrics for 15 minutes:
- Error rate (5xx responses) — should not increase. Any increase = potential rollback trigger.
- Latency (p50, p95, p99) — a jump in p99 latency often indicates a performance regression.
- Request throughput — sudden drops mean something is wrong even if error rates look normal.
- Business metrics — signups, checkouts, API calls. Technical metrics can look fine while business metrics tank.
Automate this. Don't rely on a human watching a dashboard. Set CloudWatch or Datadog alarms that trigger within 2 minutes of anomalous behavior.
Common Mistakes
- Testing in staging, not in production — staging never perfectly mirrors production. Use canary deployments to test in production safely.
- No connection draining — results in dropped requests during deployment. Always configure deregistration delay.
- Coupled database migrations — changing the schema and the application in the same deployment. Use expand-contract instead.
- No automated rollback — manual rollback requires someone to notice the problem, decide to roll back, and execute it. Automate all three.
- Deploying on Fridays — not because zero-downtime deployments are risky (they shouldn't be), but because if something subtle breaks, nobody's watching over the weekend.
Ready to Deploy Without Fear?
Zero-downtime deployments aren't about fancy tooling. They're about disciplined engineering practices — health checks, connection draining, expand-contract migrations, and automated rollbacks.
If your team still dreads deployment day, we can help. We'll audit your current deployment process and implement zero-downtime deployments — typically within a week.