Zero-Downtime Deployment: How to Update an App Without Taking It Offline

A SaaS application needs an important production update at 11 a.m.

Thousands of users are already signed in. Some are completing transactions, several long-running requests are still active, and background workers are processing jobs created minutes earlier.

The old approach would be straightforward: stop the application, install the new version, restart it and tell users to try again afterward.

Instead, the deployment begins while the existing version continues serving traffic.

New application instances start beside the old ones. They initialize, connect to dependencies and pass health checks before receiving requests. Traffic gradually moves toward them while old instances stop accepting new work and finish the requests they already hold. For a short period, both software versions are live.

Users continue working.

That is the basic idea behind zero-downtime deployment: replace or update running software without creating a planned period in which the application becomes unavailable to its users.

Kubernetes describes rolling updates in exactly these terms: existing Pods are gradually replaced by new ones so the application remains available throughout the rollout.
Kubernetes: Update a Deployment Without Downtime

But zero downtime is not achieved simply by choosing a deployment command called “rolling” or “blue-green.” The old and new versions need to coexist safely, traffic must reach only healthy instances, active requests need time to finish, database changes must remain compatible, and the team must be able to detect a bad release before it reaches everyone.

For Levelworks, this sits primarily within Seamlessly Updatable App Health, supported by Compatible, Resilient, Observable and Tested. An application is easier to maintain when releasing a routine update does not require deliberately taking the service away from its users.

Zero downtime is really a controlled handover

Traditional deployment often creates a gap:

Old version stops

        ↓

Application unavailable

        ↓

New version starts

        ↓

Application available

Zero-downtime deployment overlaps the two states:

Old version serving traffic

        ↓

New version starts alongside it

        ↓

New version becomes healthy

        ↓

Traffic transitions

        ↓

Old version drains existing work

        ↓

Old version stops

The overlap is what protects availability.

It also creates the central engineering challenge: for some period of time, more than one version of the application may be operating simultaneously.

That means the new version cannot assume that every request, database record, message or service around it has already changed.

A zero-downtime architecture is therefore partly a deployment problem and partly a compatibility problem.

[Internal Link: “Backward Compatibility: How to Update Software Without Breaking Users”]

Rolling deployment: replace instances gradually

Rolling deployment is one of the simplest approaches when an application already runs across multiple interchangeable instances.

Instead of replacing the entire fleet simultaneously, instances are updated in batches.

Suppose production normally runs six application instances:

Before

v12  v12  v12  v12  v12  v12

‍

During rollout

v13  v13  v12  v12  v12  v12

‍

Later

v13  v13  v13  v13  v12  v12

‍

After

v13  v13  v13  v13  v13  v13

At every stage, enough healthy instances remain available to serve traffic.

AWS describes rolling deployment similarly: portions of the fleet are updated while old and new versions temporarily run together, allowing updates without taking the full application offline.
AWS: Deployment Methods

Rolling deployments are attractive because they usually require less duplicate infrastructure than running two complete environments.

The trade-off is that rollback can be less clean. If half the fleet has already moved to the new version and a problem appears, production is now in a mixed state while the system reverses course.

That makes compatibility between the two versions particularly important.

Blue-green deployment: prepare the new environment before switching traffic

Blue-green deployment creates a stronger separation.

The existing production environment—commonly called blue—continues serving users while a second green environment runs the new version.

               ┌── Blue: current production

Users → Router ─┤

                └── Green: new version

The green environment can be initialized and tested before production traffic is redirected.

AWS describes blue-green deployment as maintaining two environments so the new version can be validated before traffic is switched, while the previous environment remains available for rollback.
AWS: Deployment Strategies

Azure App Service deployment slots use a similar model. Microsoft documents that an application can be deployed to a staging slot, warmed up and validated before the slot is swapped into production; the previously running production version then remains in the other slot and can be swapped back if required.
Microsoft: Azure App Service Deployment Slots

The major advantage is rollback speed: instead of reconstructing the previous release, traffic can often be directed back toward an environment that is already running.

The cost is greater infrastructure duplication during the deployment, along with the need to ensure both environments can safely interact with shared state.

Canary deployment: expose the new version to a small amount of real traffic

A green environment can pass every staging test and still behave differently with real production data or traffic.

Canary deployment reduces that uncertainty by sending only a small percentage of real traffic to the new version first.

For example:

Stage

Old version

New version

Initial

100%

0%

Canary

95%

5%

Expanded

75%

25%

Final

0%

100%

Google SRE defines canarying as a partial, time-limited deployment that is evaluated before deciding whether to continue the rollout.
Google SRE: Canarying Releases

AWS uses the same principle in current ECS canary deployments: a small percentage of traffic is shifted to the new service revision, the application is monitored during a configurable bake period, and the rest of the traffic moves only after validation.
AWS ECS: Canary Deployments

Canaries do more than protect uptime.

They reduce blast radius. If a defect affects only 5% of traffic before the rollout stops, far fewer users are exposed than in an all-at-once release.

New instances must be ready before they receive traffic

Starting a process does not mean it is ready to serve users.

A new application instance may still need to:

  • load configuration;
  • initialize dependency injection;
  • establish database connections;
  • populate local caches;
  • load models or reference data;
  • register with supporting services.

If the load balancer sends production requests too early, users can experience errors even though the deployment technically maintained multiple running instances.

This is why readiness and health checks matter.

Kubernetes rolling updates distinguish between running Pods and Pods that are actually available to receive service traffic. Its deployment controller gradually replaces old Pods while maintaining configured availability during the rollout.

Azure deployment slots similarly warm application instances before switching the slot into production, specifically to avoid cold-start-related interruption during the transition.

A useful health check should verify enough of the application's dependencies to answer:

Can this instance safely handle a real request now?

A process being alive is weaker evidence than the application being ready.

Do not kill instances that are still serving requests

Starting the new version safely is only half of the handover.

The old version must also stop safely.

Imagine a customer begins a 20-second report request on Version 12.

Five seconds later, deployment removes that instance immediately.

The application technically had other healthy servers available, but the customer's in-progress connection has already been terminated.

Proper connection draining stops routing new requests to an instance while allowing existing connections or work to complete before the process exits.

Kubernetes documents this termination behavior explicitly: terminating endpoints are marked as not ready for ordinary traffic, while the application and load-balancing layer can coordinate graceful connection draining before the Pod disappears.
Kubernetes: Pod Termination and Connection Draining

This becomes especially important for:

  • long HTTP requests;
  • WebSocket connections;
  • uploads;
  • streaming responses;
  • background workers processing nontrivial jobs.

Zero downtime should cover users already doing work, not only users whose requests arrive after the deployment begins.

Old and new application versions must understand the same database

Database changes are one of the easiest ways to turn a nominally zero-downtime deployment into a real outage.

Suppose Version 12 reads:

customers.full_name

Version 13 replaces it with:

customers.first_name

customers.last_name

If the deployment immediately drops full_name, Version 12 instances still operating during the rollout can fail.

The safer pattern is to separate expansion from removal.

First, introduce database changes that the old application can tolerate. Then deploy code that can work with the new structure. Only after the old version is completely gone should obsolete schema elements be removed.

AWS's blue-green deployment guidance explicitly recommends decoupling schema changes from application-code changes and keeping database updates backward-compatible while both old and new versions may use the database.
AWS: Managing Schema Changes in Blue-Green Deployments

Microsoft likewise recommends backward-compatible database changes during gradual production rollouts—for example, adding new columns or tables without immediately removing structures the previous version still needs.
Microsoft: Execute Cloud Modernization Safely

Zero-downtime deployment therefore often favors expand → migrate → contract rather than destructive one-step schema changes.

Session state cannot be trapped inside the instance being replaced

Imagine a user's authenticated session exists only in the memory of Server A.

During a rolling deployment, Server A is removed.

The next request reaches Server B, which knows nothing about the session.

From an infrastructure perspective, the deployment succeeded.

From the user's perspective, they were unexpectedly logged out—or worse, a multi-step workflow was lost.

Applications that need frequent instance replacement are easier to deploy when important session state is not tied exclusively to one process.

Depending on the architecture, that can mean externally stored session data, stateless authentication tokens, shared persistence or another design that lets a different healthy instance continue handling the user's requests.

The precise solution varies, but the requirement is consistent:

Removing one application instance must not remove user state that another instance cannot reconstruct.

Static assets and APIs need compatibility during the transition too

Mixed-version deployment can create less obvious problems in web applications.

Suppose a user's browser already loaded the Version 12 frontend.

Version 13 of the backend then goes live.

The old JavaScript client may continue making requests for minutes or hours depending on how the application behaves.

If Version 13 immediately removes an API field that Version 12 JavaScript expects, active users can break even though the new frontend works perfectly.

Cached assets create similar issues.

A new HTML document could reference Version 13 JavaScript while a stale cache supplies an older incompatible resource.

Common protections include immutable versioned asset filenames and backward-compatible API changes during rollout windows.

The same reasoning applies to mobile apps even more strongly because older clients may remain installed for months.

Zero downtime therefore depends on contract compatibility, not merely infrastructure redundancy.

Monitor the new version separately from the old one

Suppose overall production error rate remains 0.5%.

That sounds healthy.

But production traffic is currently:

Version 12 → 95% of requests

Version 13 → 5% of requests

If Version 13 is failing 10% of the time, the aggregate metric can initially hide the problem.

Deployment monitoring should therefore distinguish the new revision from the control.

Useful comparison signals include:

Signal

What to compare

Error rate

New vs current release

p95/p99 latency

New vs current release

Resource use

CPU, memory, connections

Business success

Checkout, login, upload, etc.

Dependency failures

Database and downstream calls

Google's canary guidance emphasizes evaluating the canary against the unaffected control before proceeding with rollout.

AWS Well-Architected similarly recommends safe rollout strategies that validate outcomes while limiting how much of production is affected if the release contains defects.
AWS Well-Architected: Employ Safe Deployment Strategies

This is where Observable App Health becomes essential to Seamlessly Updatable App Health. A gradual rollout provides little protection if nobody can tell whether the new version is behaving worse.

Rollback needs to remain possible throughout the critical part of the rollout

A deployment strategy is only as safe as its recovery path.

Blue-green deployment has a strong rollback property because the previous environment can remain available while the new one is evaluated.

Azure's deployment-slot documentation notes that after a production swap, the previous production application occupies the staging slot, allowing teams to swap it back if the release behaves unexpectedly.

Rolling deployments can make reversal more involved because some instances already run the new version while others may still be old.

Database changes complicate rollback further.

If Version 13 writes data in a format Version 12 cannot read, simply sending traffic back to Version 12 may fail.

This is another reason destructive database changes should usually happen only after confidence in the new application version is high.

The safest deployment is not merely one that can roll forward without downtime. It preserves a usable route backward until the team has enough evidence that the new version should remain.

Zero downtime does not mean zero release risk

There is an important distinction between:

no planned unavailability

and

no possibility of user impact.

A canary may still expose some users to a bug. A database migration might briefly increase latency, while an external dependency may react differently to the new release.

Even Azure's reliability guidance distinguishes zero-downtime deployment mechanisms from broader application resilience concerns. Deployment slots can prevent the production application from being taken offline during replacement, but the application still needs correct health checks, configuration and runtime behavior.
Microsoft: Reliability in Azure App Service

Zero downtime is therefore an engineering objective:

users should continue receiving service while application versions change.

It is not a claim that releases can no longer contain defects.

When a maintenance window may still be the safer choice

Not every change should be forced into a zero-downtime model.

Some operations may involve:

  • destructive data transformations;
  • changes that cannot support old and new versions concurrently;
  • infrastructure changes without a safe parallel path;
  • legacy applications designed around a single instance;
  • migrations whose rollback properties are poorly understood.

Trying to conceal such a change inside an elaborate “zero-downtime” procedure can create more risk than scheduling a controlled period of reduced availability.

The correct objective is not zero downtime at any cost.

It is minimizing user disruption without making the change less safe.

[Internal Link: “What Is a Maintenance Window and How Should You Schedule One?”]

A practical zero-downtime deployment sequence

A reliable release typically follows this progression:

  1. Build and test a known release artifact. The version being deployed should be traceable and already have passed the required automated checks.
  2. Make shared-state changes backward-compatible. Database schemas, APIs and message formats must tolerate both versions during the transition.
  3. Start the new version beside the old one. Do not remove healthy production capacity first.
  4. Warm and validate new instances. They should pass readiness checks before receiving real traffic.
  5. Send limited traffic to the new release. Where risk justifies it, use a canary or similarly gradual rollout.
  6. Compare new-version health with the existing version. Watch technical and business signals rather than simply checking that processes are running.
  7. Increase traffic gradually. Continue only while the new release remains healthy.
  8. Drain old instances gracefully. Stop new requests while allowing active work to complete.
  9. Keep rollback available until confidence is high. Avoid destroying the old environment or old-compatible schema prematurely.
  10. Remove transitional compatibility later. Once the previous version can no longer return, obsolete fields, code paths and infrastructure can be cleaned up.

[Internal Link: “CI/CD for App Maintenance: How Automated Releases Reduce Risk”]

Automation is particularly valuable here because each step depends on timing, validation and consistent execution. Manual zero-downtime releases can become intricate enough that the release process itself introduces risk.

Zero downtime is designed into the update path

The most important lesson is that zero-downtime deployment cannot be added at the last minute by changing a deployment setting.

It depends on several application properties working together.

There must be enough redundancy for one version to remain available while another starts. New instances must prove readiness before receiving users, while retiring instances need time to finish work. Old and new releases have to coexist safely against the same APIs, databases and stored state, and production monitoring must reveal whether the new version behaves differently before the rollout reaches everyone.

For Levelworks, that is what Seamlessly Updatable App Health means in practice.

It is reinforced by Compatible, because neighboring versions must coexist during transitions; Observable, because deployment decisions depend on production evidence; and Resilient, because a failed release should be contained rather than turning into a full application outage.

Zero-downtime deployment is therefore not really about eliminating a maintenance screen.

It is about changing the release model from:

“Take the old application down, then hope the new one starts correctly.”

to:

“Bring the new version up, prove that it is healthy, move users toward it safely, and retire the old version only when it is no longer needed.”

When an application can do that repeatedly, updating production stops being an interruption that users have to accommodate and becomes a controlled transition the application is designed to survive.

‍

Continue Reading