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.

By Danyal Ahmed - Data Scientist · · 4 min read

"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:

  1. At least two instances behind a load balancer (how load balancers work).
  2. Readiness checks: new instances receive traffic only when actually ready.
  3. Graceful shutdown: on SIGTERM, stop accepting new requests, finish in-flight ones, close connections, then exit.
  4. Connection draining at the load balancer.
  5. 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.