<!-- https://zerolog.ch/docs/zero-downtime-deploy · Docs / Research and security -->
# Zero-downtime deploys

A release starts the new code next to the old code and then moves traffic over. You are not sent through a restart: a reply that is already streaming, or a file upload that is already under way, finishes on the old copy.

## How a release goes

1. **Test in isolation.** The new code is tested in an isolated copy first: lint and the test suite. Only green code moves on.
2. **Back up.** A backup is taken before anything on the live system changes.
3. **Check the database changes.** Old and new copy share one database for a few moments, so a release that drops or renames a table or column, or adds a required column without a default, is refused.
4. **Start the idle slot.** It starts with the new code. If it does not pass its health check, the active slot keeps serving and the release stops there.
5. **Move traffic.** The new slot is activated and the front-door proxy is told to send traffic to it. If the proxy's own code changed, a second proxy instance starts, is health-checked, and the port redirect is moved to it before the old instance stops.
6. **Drain the old slot.** Follow-up requests of chats and uploads that began on it are kept on it until they finish; it is stopped after at most ten minutes.
7. **Restart the worker.** The background worker restarts and finishes its running jobs first, for at most five minutes.
8. **Smoke test.** A smoke test runs against the slot that is now active.

## What it does not do

A reply that is still running when the old copy's ten minutes are up is cut off. A release that removes or renames database columns is refused in the normal path and needs a planned two-step release instead. None of this is something you configure.

## Going back

The operator can return to the single-process setup. The old process starts and is health-checked while the proxy keeps serving; only then does traffic move to it, and the proxies and slots are removed last.

Related: [Isolation](https://zerolog.ch/docs/isolation), [Trust and security](https://zerolog.ch/docs/trust), [Status](https://zerolog.ch/status).

## How a deploy switches ZeroLog to new code without downtime

Mermaid source of the diagram on this page:

```mermaid
%% Zero-downtime deploy. Facts from scripts/deploy.sh, scripts/zdt-proxy.mjs, scripts/zdt-nft.sh, scripts/zdt-worker-cutover.sh, scripts/zdt-rueckbau.sh.
flowchart TB
  visitors["Visitors, tunnel and<br/>worker machines<br/>(all use port 8080)"]
  nft["nftables table zl_zdt<br/>redirects port 8080<br/>to the active proxy"]

  subgraph PX["Front-door proxy (Node.js)"]
    direction LR
    pa["zerolog-proxy@a<br/>port 8084"]
    pb["zerolog-proxy@b<br/>port 8085"]
  end

  subgraph SL["App slots, one active"]
    direction LR
    blue["zerolog@blue<br/>port 8082"]
    green["zerolog@green<br/>port 8083"]
  end

  deploy["deploy.sh<br/>tests, backup, switch"]
  worker["zerolog-worker<br/>background jobs"]

  visitors --> nft
  nft -->|one instance at a time| PX
  PX -->|active slot only| SL
  deploy -->|start idle slot,<br/>activate| SL
  deploy -->|reload proxy| PX
  deploy -->|restart,<br/>with drain| worker

  linkStyle 3 stroke:#8f7ac9
```
