Azure & Cloud Architecture

Zero-Downtime Deployment Strategies for ASP.NET Core

Zero downtime is a compatibility property across application instances, databases, messages and clients. Swapping a slot or rolling pods cannot compensate for a schema or behaviour that only one version understands.

Zero-downtime deployment sequence

Deployment overlap is the normal state

During a rolling release, old and new instances serve traffic together. Clients, queued messages and caches may remain older still. Every release must tolerate this compatibility window. “Deploy all at once” is rarely atomic once load balancers and process startup are involved.

Strategies and hidden assumptions

StrategyBenefitAssumption
RollingGradual capacity replacementVersions interoperate
App Service slot swapWarm candidate and fast traffic switchSlot settings and database are compatible
Blue/greenFast routing rollbackShared state supports both environments
CanaryLimits initial exposureTelemetry identifies cohort outcomes
Cloud application deployment with no interruption
Traffic movement is the easy part; compatibility and irreversible state determine whether rollback is real.

Readiness protects traffic; liveness protects process recovery

Readiness should remain false until configuration is valid, required startup work is complete and the instance can serve. Remove readiness before shutdown so traffic drains. Liveness should detect a process that cannot recover, not restart every instance during a shared database outage.

Keep load-balancer probes cheap and side-effect free. Warm critical paths separately; a 200 from /health does not prove first-request compilation, key loading or route behaviour is ready.

Use expand-and-contract database changes

Add new nullable columns, tables or indexes first. Deploy code that can work with old and new shapes, backfill in bounded batches, switch reads/writes, then remove old structures in a later release. Renaming a column in one migration is drop-and-add from the old application's perspective.

Do not let every instance race to run destructive migrations at startup. Use a controlled migration job with ownership, timeout and observability. Large index or backfill operations need production lock and log-volume planning.

Message contracts outlive instances

A message emitted before deployment may be consumed afterward. Make consumers tolerant, version incompatible contracts and retain handlers until old messages cannot remain in queues, retries or dead letters. A rollback may produce old messages again.

Jobs in progress need drain or lease behaviour. Killing a worker after traffic switches can duplicate work when locks expire; handlers must be idempotent regardless.

Slots are environments, not magic transactions

App Service deployment slots allow warm-up and swap, but settings marked sticky remain with their slot while others swap. Verify identities, connection strings, hostnames and external callbacks. The staging slot may accidentally process production queues or scheduled jobs before swap.

Warm the actual application through a safe endpoint and validate from the slot hostname without triggering user-visible effects. Slot rollback moves traffic, not database changes or external side effects.

Feature flags separate release from deployment

Deploy dormant compatible code, then enable by tenant or percentage while observing outcomes. Evaluate a flag consistently for a workflow; changing halfway through can create hybrid state. Remove the flag after the fallback window.

A kill switch can reduce incident impact, but it must provide a designed degraded path. Flags do not make destructive migrations reversible.

Rollback is constrained by state

Application binaries are easy to restore. New writes, schema contraction, messages, cache formats and third-party side effects may not be. Before release, state what the old version will do with data produced by the new one.

Sometimes roll-forward is safer than rollback. Define decision thresholds, owner and maximum diagnosis window in advance. A “rollback plan” consisting of clicking the previous pipeline run is not enough.

Sessions and real-time connections need drain semantics

WebSockets, SignalR circuits and long requests can remain attached to old instances. Decide whether to drain, reconnect or terminate after a deadline. In-memory session state prevents seamless movement; externalise only state that genuinely must survive and protect it across versions.

Canary on outcomes, not server health

Compare error codes, latency and business completion for canary and baseline cohorts. Overall averages hide a small broken cohort. Use stable routing where multi-request journeys require one version, but do not rely on affinity to excuse incompatible shared data.

Automate pause and rollback for clear technical thresholds while keeping human ownership for ambiguous business signals.

Production checklist

  • Old and new instances accept each other's data and contracts.
  • Readiness gates traffic and shutdown drains it.
  • Schema uses expand, migrate, contract across releases.
  • Migrations run once through controlled automation.
  • Queued messages and cache values are version-tolerant.
  • Slots cannot consume production work before intended.
  • Flags have deterministic rollout and removal.
  • Rollback accounts for irreversible state and side effects.
  • Long connections have a drain/reconnect policy.
  • Canary metrics include business outcomes.

Compatibility matters in both directions

During rolling deployment, old and new instances run together. New code must tolerate old data and messages, but rollback also means old code may see data written by the new version. Forward compatibility is the frequently missed half.

Additive contracts are safer: add optional fields, accept unknown values and delay removal until every producer and consumer has moved. Changing enum meaning or reusing a field is more dangerous than adding a version.

Map the maximum overlap: web instances may drain in minutes, queued messages may live for days and mobile or external API clients may persist for months. “Deployment complete” is not the end of contract compatibility.

Database migrations need their own release sequence

Run online-safe schema changes separately from application startup where locks and duration are controlled. Adding an index to a large table or rewriting every row can exceed the deployment window even when the SQL is syntactically simple.

Use expand, migrate and contract: introduce compatible schema, deploy code that supports both, backfill in bounded resumable batches, verify, switch usage and remove later. Dual writes need monitoring and reconciliation because one write path can fail.

Do not let every new instance race to run migrations. Use a controlled deployment step with ownership and evidence, while retaining application compatibility if migration is delayed.

Warm-up must exercise the real dependency graph

A process can answer a shallow health endpoint while first-request compilation, configuration loading or connection creation still causes timeouts. Warm the routes and caches necessary for service without mutating customer data.

Readiness should fail until the instance can accept intended traffic. Keep liveness narrower so a temporary external dependency outage does not restart every healthy process and amplify failure.

Health checks should be fast, bounded and protected from expensive work. A probe that depends on every optional supplier converts their outage into your total removal from load balancing.

Connection draining is part of zero downtime

Removing an instance from new traffic does not finish in-flight requests, streaming responses, WebSockets or background work. Configure a termination grace period and make the application stop accepting new work before shutdown.

For SignalR and other long-lived connections, clients need reconnect behaviour and state must not live only inside one process. Sticky sessions reduce movement but do not make deployments safe when an instance eventually terminates.

Background workers should stop fetching new messages, finish within the lock and cancellation budget, or abandon safely for redelivery. Forced termination makes idempotency essential.

Canaries need comparable and attributable evidence

Expose a small representative cohort and tag telemetry with deployment version. Compare errors, latency, business completion and dependency behaviour against the stable population. Aggregate metrics without version cannot show which release caused deterioration.

A tiny canary may miss tenant-specific configuration or low-volume workflows. Choose cohorts covering meaningful risk and include synthetic tests for critical paths. Do not promote only because CPU and HTTP 200 rates look normal.

Define automatic halt thresholds and a human decision owner. Canary deployment without a response plan only discovers incidents gradually.

Feature flags create a second deployment system

Flags can expose code independently of binaries, but changing one can have production impact as large as a release. Require validation, audit, progressive targeting and rollback for high-risk flags.

Keep old and new paths compatible with the same data while the flag varies across users or instances. A flag that changes schema assumptions cannot safely roll out as a presentation toggle.

Every temporary flag needs expiry and removal. Permanent stale paths multiply combinations and make later deployments less safe-the opposite of the reason flags were introduced.

Rollback cannot undo external reality

A previous binary does not reverse messages already emitted, emails sent, provider calls completed or new-format data written. Decide which changes are reversible, compensatable or forward-fix only before rollout.

Keep stable operation IDs and audit release version with important side effects. Incident responders need to identify work produced by the faulty version and reconcile it rather than merely restore traffic.

Practise rollback and forward correction in non-production with realistic data volume. A rollback command that has never met a changed schema is a hope, not a strategy.

Release observability starts before traffic moves

Record deployment, migration, configuration and flag changes as events in operational timelines. Correlate requests and background work with version, region and slot. Otherwise responders infer change from timestamps during an incident.

Watch business outcomes, saturation, queue age and reconciliation as well as exceptions. Some regressions return successful but wrong or incomplete results.

Zero downtime is not “no 503s during swap”. It is preserving correct service and recoverable state throughout overlapping versions, traffic movement and operational response.

Related C3 Software guides

Frequently asked questions

Do deployment slots guarantee zero downtime?

No. They help warm and switch application instances; database, messages, settings and side effects still require compatibility.

Should applications run migrations at startup?

Prefer a controlled single migration job for production, especially for destructive or long-running changes.

Can feature flags replace API versioning?

No. Flags stage behaviour; consumers that depend on different contracts need explicit compatibility.

When is rollback unsafe?

When new code has produced data, messages or external effects the old version cannot interpret or reverse.

Make releases compatible before moving traffic

C3 Software Limited has built and supported business software in the UK since 2001. For deployment reviews, talk to C3 Software.