Zero-Downtime Deployments: Rolling, Blue-Green, Canary and Feature Flags
How to deploy without downtime — rolling updates, blue-green, canary releases, feature flags, graceful shutdown, health checks, backward-compatible database migrations and rollback.
"We'll deploy Saturday at 2 a.m. when traffic is low" is a sign of a fragile release process. Modern teams deploy during business hours, often many times a day, without users noticing. Zero-downtime deployment isn't magic. It's a set of well-understood techniques that keep the service available while new versions roll out.
Key takeaways
- Zero downtime needs: multiple instances, health checks, graceful shutdown and backward-compatible changes.
- Strategies: rolling updates (simple default), blue-green (instant switch and rollback), canary (gradual, metric-driven).
- Feature flags separate deploying code from releasing features.
- Database migrations are the hardest part. Use the expand–contract pattern.
- Automate verification and rollback. A fast rollback beats a perfect deploy.
Prerequisites
Before any strategy works:
- At least two instances behind a load balancer (how load balancers work).
- Readiness checks: new instances receive traffic only when actually ready.
- Graceful shutdown: on SIGTERM, stop accepting new requests, finish in-flight ones, close connections, then exit.
- Connection draining at the load balancer.
- Backward compatibility: during rollout, old and new versions run side by side, sharing the same database and talking to the same clients.
python# Graceful shutdown sketch (framework-agnostic)
import signal, sys
shutting_down = False
def handle_sigterm(*_):
global shutting_down
shutting_down = True # readiness endpoint starts returning 503
server.stop_accepting() # stop taking new connections
server.wait_for_inflight(timeout=25)
db_pool.close()
sys.exit(0)
signal.signal(signal.SIGTERM, handle_sigterm)
Strategy 1: Rolling updates
Replace instances a few at a time:
textv1 v1 v1 v1 → v2 v1 v1 v1 → v2 v2 v1 v1 → v2 v2 v2 v2
- ✅ Simple, resource-efficient; the default in Kubernetes Deployments and many platforms (Kubernetes explained).
- ⚠️ Mixed versions serve traffic during the rollout; rollback is another rolling update, so it's slower.
- Tune max unavailable (for example, 0) and max surge so capacity never drops.
Strategy 2: Blue-green
Run two complete environments. Blue serves production; deploy the new version to green, test it, then switch traffic:
text ┌──▶ Blue (v1) ← live
Router ────┤
└──▶ Green (v2) ← deployed, tested, then traffic switched here
- ✅ Instant cutover and instant rollback (switch back to blue).
- ✅ Full testing of the new environment before it takes traffic.
- ⚠️ Double the infrastructure during deployment.
- ⚠️ Both environments usually share the database, so schema changes must be compatible with both.
Strategy 3: Canary releases
Route a small percentage of traffic to the new version, compare its health with the stable version, and expand gradually:
text1% → 5% → 25% → 50% → 100% (pause and evaluate at each step)
- ✅ Limits the blast radius of bad releases: only a fraction of users are affected.
- ✅ Real production validation with real traffic.
- ⚠️ Requires good metrics and traffic-splitting capability.
Automated canary analysis compares error rates, latency percentiles and key business metrics between canary and baseline, and rolls back automatically if thresholds are breached (observability explained).
Feature flags: deploy ≠ release
Feature flags let you ship code to production turned off, then enable it for internal users, beta customers, a percentage of users, or everyone, without redeploying:
tsif (flags.isEnabled("new-checkout", { userId, plan })) {
return renderNewCheckout();
}
return renderLegacyCheckout();
Benefits:
- Merge incomplete work safely (trunk-based development).
- Instant kill switch for problematic features.
- Gradual rollouts and A/B tests (A/B testing done right).
Discipline required:
- Remove flags after rollout. Stale flags become technical debt (technical debt management).
- Name and document flags, with owners and expiry dates.
- Test both flag states.
Database migrations: the expand–contract pattern
Code rolls back easily; data doesn't. During any rollout, old and new code run simultaneously, so every schema change must work with both.
Example: renaming users.fullname to users.display_name.
text1. EXPAND: add column display_name (nullable). Deploy code that writes BOTH columns,
reads the old one.
2. MIGRATE: backfill display_name for existing rows (in batches, to avoid locking).
3. SWITCH: deploy code that reads display_name (still writing both).
4. CONTRACT: once no running code uses fullname, stop writing it; later, drop the column.
Rules of thumb:
- Never rename or drop columns in the same deploy that changes the code using them.
- Add columns as nullable or with safe defaults.
- Create indexes without locking tables where your database supports it (how database indexes work).
- Backfill in batches with throttling.
- Run migrations as a separate, observable step in your pipeline (CI/CD pipeline guide).
API and client compatibility
- Make API changes additive; version or deprecate breaking changes (API design best practices).
- Remember mobile apps and cached frontends may run old versions for weeks.
- For message queues and events, keep schemas backward-compatible so old consumers can read new messages, and vice versa (event-driven architecture).
Verification and rollback
After (and during) every deploy:
- Smoke tests on critical paths.
- Watch key metrics: error rate, latency and business KPIs, with deploy markers on dashboards.
- Automatic rollback on SLO breaches during canary or rollout.
- Rollback drills: practice them so they're routine.
Keep the previous version's artifact ready. Rollback should be a single command or click.
Choosing a strategy
| Situation | Good fit |
|---|---|
| Most web services on Kubernetes/PaaS | Rolling updates + feature flags |
| Need instant rollback, can afford 2× capacity briefly | Blue-green |
| High traffic, high risk of regressions | Canary with automated analysis |
| Risky features, experiments, gradual exposure | Feature flags (with any deploy strategy) |
These combine naturally: rolling or canary deploys for code, feature flags for features, expand–contract for data.
FAQ
What's the difference between blue-green and canary deployment?
Blue-green switches all traffic from the old environment to the new one at once (with instant switch-back). Canary gradually shifts a percentage of traffic to the new version while comparing metrics, limiting blast radius.
Can small teams do zero-downtime deployments?
Yes. Running two instances behind a load balancer (or a platform that does rolling updates), health checks, graceful shutdown and backward-compatible migrations are enough for most applications.
How do feature flags relate to deployments?
Deployments put new code on servers; feature flags control whether that code's behavior is active for users. This separation makes releases safer and reversible without redeploying.
What's the hardest part of zero-downtime deployments?
Usually database schema changes and data migrations, because they're shared between old and new versions and are hard to roll back. The expand–contract pattern addresses this.