Skip to main content

Coolify's Zero-Downtime Asterisk: Why Every Docker Compose Deploy Still Goes Down

11 min readDora NodaDora Noda
Share
On this page

Coolify will deploy your Dockerfile without dropping a single request — and then take your Docker Compose stack fully offline to deploy that. Both behaviors ship in the same v4 binary, behind the same green "Deploy" button, and the difference between them is the single most important thing to understand before you put a production workload on the category's most popular self-hosted PaaS.

The numbers tell you how many people are exposed to the gap. Coolify passed 55,000 GitHub stars and shipped v4.0.0 stable in April 2026, making it the default answer to "I want my own Heroku."

Its zero-downtime story is real for single-container apps: configure a healthcheck, and Coolify starts the new container, waits for it to go healthy, and only then swaps traffic over. But point that same platform at a docker-compose.yml — the thing a real app (web + worker + database) actually is — and every deploy becomes stop-the-world: the old stack comes down before the new one is healthy, Traefik has nothing to route to, and your users get a 502 window of roughly 10 to 30 seconds. Here is the support matrix, up front, because it is the whole post in one table:

Deploy sourceCoolify zero-downtime?What actually happens on deploy
Dockerfile / Nixpacks / single imageYes, with conditionsNew container starts, healthcheck gates the traffic swap, old container drains
Docker Compose stackNoOld containers stop first, new ones start after — a 502 gap every deploy
Swarm-mode servicesRolling, but experimentalDocker Swarm rolling updates work — on an integration Coolify has deprecated ahead of v5

This post is the mechanical explanation of that table: what a Compose deploy does step by step, why zero downtime is nearly free once an orchestrator owns the rollout and genuinely hard to bolt onto docker compose up -d after the fact, what the closing Swarm escape hatch means next to Dokploy's first-class Swarm support — and the sharper question to ask any self-hosted PaaS before you commit.

What a Compose deploy actually does, second by second

Take the canonical small-production stack: a web service behind Traefik, a background worker chewing a queue, and Postgres with a named volume. You push to main. Here is what Coolify's Compose buildpack does with your push:

  1. Stop the running stack. The old web and worker containers get SIGTERM, then SIGKILL after the grace period. In-flight HTTP requests die mid-flight. Jobs the worker already dequeued but hasn't finished are interrupted — completed from the queue's perspective, lost from yours, unless every job handler is idempotent and every queue redelivers.
  2. Recreate and start the new containers. Images pull, containers create, entrypoints run, migrations (if any) execute. Your app's boot time — framework load, connection-pool warmup, JIT — all happens while nothing is serving traffic, because there is nothing left to serve it.
  3. Traefik routes to whatever exists. During the window between step 1 and a healthy step 2, the reverse proxy has no healthy backend and answers 502. Operator-run notes consistently put this window at 10 to 30 seconds for typical stacks — longer if an image pull is cold or a migration runs.

Note what is missing: at no point do old and new exist simultaneously, so there is nothing to gate on and nothing to drain from. The deploy is a restart with extra steps.

Contrast that with what Coolify does for a single-container app when all four of its conditions hold — single container (not Compose), no exclusive host-port binding, a healthcheck that passes reliably, and volumes (if any) attachable to two containers at once. Then it performs a genuine rolling swap: build and start the replacement alongside the old one, wait for the healthcheck to go green, move traffic, then stop the predecessor. Failed healthcheck means traffic never moves — an automatic rollback by construction. That is real zero-downtime machinery. It just only exists on one side of the deploy-source table, and true Compose zero downtime is officially a v5 roadmap item, not a v4 feature.

The worker half of the story deserves its own sentence because status pages never show it. A 502 window is visible and retryable; a SIGTERMed worker is silent. Every Compose deploy is a small unclean shutdown of every background process you run, and "make every job idempotent and every queue redelivering" is a tax the platform quietly levies on your application code to compensate for a deploy primitive it doesn't have.

Why orchestrators get this nearly free

Zero-downtime deploys look like a feature. They are actually three primitives composed, and the composition only works when one system owns the whole rollout:

  1. Start-new-before-stopping-old. Kubernetes Deployments express this as maxSurge (how many extra pods may exist during the rollout) and maxUnavailable (how many may be down — zero, if you mean it). Docker Swarm expresses the same idea as update_config with order: start-first. Either way, the new version exists and is running before the old version is touched. docker compose up -d, by contrast, is a recreate loop with no surplus capacity stage: it has nowhere to put a new container while the old one still holds the name, the network alias, and the port.
  2. A readiness gate between "running" and "receiving traffic." A container that has started is not a container that is ready — the framework may still be loading, the pool still warming. Kubernetes keeps new pods out of Service endpoints until their readiness probe passes; Swarm waits on its healthcheck before considering the update successful. Coolify's single-container swap reuses exactly this gate. A Compose stack has per-service healthchecks on paper, but nothing in the Compose model sequences traffic across the stack boundary: the proxy doesn't know the new web container isn't ready, and nothing tells the worker to hold off dequeuing until migrations finish.
  3. Draining the old instead of killing it. Kubernetes sends SIGTERM, removes the pod from endpoints, then waits out terminationGracePeriodSeconds — often with a short preStop sleep so the proxy's stale endpoints expire — before SIGKILL. In-flight requests finish; the queue consumer stops fetching new work and completes what's in hand. Compose teardown sends the same SIGTERM but with traffic still pointed at the dying container and no orchestrated handoff, so the signal reads less like "finish up" and more like "stop immediately, if you can."

A Kubernetes Deployment gets all three by definition — they are the object's reason for existing. That is the content of "container runner versus orchestrator": a runner starts containers; an orchestrator owns the transitions between versions of them. Every zero-downtime story is a transition story, which is why the capability is structural, not a checkbox a runner can fill in with more code around the same recreate loop.

The escape hatch that's closing

The obvious objection — "just run the Compose stack on Swarm, which has rolling updates" — is where the comparison with Dokploy gets interesting, and where Coolify's direction of travel matters more than its current docs.

Coolify does have Swarm support, and Swarm genuinely has the start-first/update-config machinery from the previous section. But Coolify's Swarm integration is labeled experimental, and the label recently got worse: a v4 maintenance change marks Docker Swarm support as deprecated, with v5 slated to remove Swarm entirely and replace it with native Compose replicas plus Coolify's own scaling solution — automatically migrating existing Swarm resources to plain standalone Compose during the v5 upgrade.

The "experimental" tag was never about Swarm itself (Mirantis supports Swarm through 2030); it was about the completeness of Coolify's integration. And that integration is now heading for removal, not completion. Whatever rolling behavior Swarm-backed Coolify apps enjoy today is living on borrowed time.

Dokploy, the category's second-largest project at roughly 34,000 stars against Coolify's 55,000-plus (per a May 2026 side-by-side that also pegged the stables at v4.0.0 and v0.29.4 respectively), made the opposite bet: Swarm rolling updates are a documented, first-class setting. Its zero-downtime docs walk you through Advanced → Swarm Settings healthchecks — the update parallelism, delay, and failure-action knobs that make a rolling deploy actually roll — and its users reason about start-first ordering as a normal part of operating the platform rather than an experimental detour.

Honesty requires the caveats on Dokploy's side too, because Swarm-first is not free either. Swarm's scheduler ignores Compose-isms it doesn't implement — depends_on is the classic casualty, so startup ordering you tested locally with docker compose up silently stops being enforced after migration, and teams end up splitting seeding and migration logic to compensate. Swarm-only keys like update_config are likewise ignored by plain Compose, so a stack tuned for Dokploy's rolling behavior doesn't compose up the same way on a laptop. And Swarm itself is a shrinking ecosystem: it works, it is supported, but the industry's orchestration mindshare left years ago, which is presumably part of why Coolify is walking away from it. Dokploy's answer is coherent but it is a bet on a stable, unfashionable substrate — eyes open.

The net for a team choosing today: Coolify gives you the bigger community, the faster-moving project, and rolling deploys only for single-container apps, with the Swarm back door visibly closing. Dokploy gives you rolling Compose deploys now, on infrastructure the rest of the industry has stopped investing in. Neither gives you an orchestrator.

The sharper question

Feature matrices will tell you both platforms "support Docker Compose" and both "support zero-downtime deploys," and both statements are true in the sense that fails you. The question that actually discriminates is per-source, not per-platform: for each deploy source I will use, walk me through the thirty seconds around a deploy. Then check the four things that decide whether the answer involves downtime:

  • Which sources get the rolling path? If the answer is "Dockerfile yes, Compose no," price the 502 window at your deploy frequency. Ten deploys a day at 20 seconds apiece is over an hour of degraded time every month — scheduled, self-inflicted, and invisible in every uptime metric that samples slower than your deploy cadence.
  • What gates the traffic swap? A healthcheck the platform waits on before routing (Coolify's single-container behavior, Swarm healthchecks, Kubernetes readiness) is a guarantee; a container-state check ("is it running") is a hope. Ask specifically what happens when the new version boots but never becomes ready.
  • What does the proxy serve during the transition? Somebody answers for your domain while backends churn — Traefik, Caddy, an ingress controller. Find out whether "no healthy backend" renders as a fast 502 your clients retry or a hung connection they don't, and whether the old version keeps serving until the new one is proven.
  • What forces the recreate fallback? Exclusive host ports, single-attach volumes, multi-service stacks — every platform has a list of conditions that silently downgrade rolling to restart. Get the list in writing before you design your storage and networking around the platform, not after your first stateful deploy drops.

A platform built on Kubernetes answers all four without thinking, because the Deployment object is the answer: surplus capacity, readiness gates, endpoint draining, and per-service independent rollout are the default rather than the exception.

That is not an argument that every team needs Kubernetes — it is the standard against which to measure what a simpler runner is asking you to give up: roughly, one 502 window and one unclean worker shutdown per Compose deploy, forever, plus application-level idempotency work to paper over the shutdowns. For a side project deployed twice a week, that price rounds to zero, and Coolify's velocity and ecosystem are worth more than its rollout semantics. For a multi-service app deployed ten times a day with background jobs that must not double-fire, it is the whole evaluation — and no checkbox on a feature matrix will tell you.

Deploy on machines you own with orchestrator-grade rollouts: Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on your own hardware, where every deploy is a rolling update with readiness gates and connection draining, not a restart. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex