"Zero-downtime deploy" is a phrase that gets used loosely. Here's exactly what happens, step by step, when DeployOS deploys a new version — because the mechanism is what actually determines whether a bad deploy takes your app down.
Step 1: build without touching production
The new image builds (via Railpack or your Dockerfile) while the currently-running container keeps serving every request. Nothing about building an image requires stopping anything.
Step 2: start the new version alongside the old one
The freshly built container starts on the same Docker network as the running one, under a different internal name. At this point there are two versions of your app running simultaneously, but only the old one is receiving public traffic — the new one is reachable only inside the network, not yet routed to.
Step 3: health-check before anything changes publicly
DeployOS probes the new container's health endpoint directly, container-to-container, with a retry budget. If your app doesn't define a custom health check, it checks that the port responds at all. This is a soft gate for apps without one — a check that doesn't succeed logs a clear warning rather than silently blocking, since not every app framework makes a health check trivial to expose.
Step 4: atomic route switch
Only once the health check passes does the reverse proxy (Traefik or Caddy) get told to route to the new container instead of the old one. This switch is effectively instant from the proxy's perspective — there's no window where requests hit a stopped container.
Step 5: the old version stays around, on purpose
The previous container isn't torn down immediately. It stays available so that if something looks wrong after the switch — a bug that only shows up under real traffic, not the health check — rollback doesn't require a rebuild.
Rollback: pointing back, not rebuilding
Because images are tagged by commit hash and the last several builds are kept on the server, rollback is "start the previous tagged image and switch the route back," the same atomic switch as step 4 in reverse. No git revert, no waiting for a build, no downtime for the rollback itself either.
What this actually protects against
This sequence specifically defends against the two most common ways a deploy goes wrong: a build that fails outright (never reaches step 4, old version never stops serving) and a build that succeeds but crashes or misbehaves under real load (caught either by the health check or by a fast manual rollback once it's visible). It does not replace real testing — a bug that passes health checks and only shows up later still needs a human to notice and roll back. But it removes "the deploy mechanism itself caused the outage" as a failure mode entirely.
Read more on the reasoning behind this pattern in Zero-Downtime Deployments on a Single VPS.