A git push lands a running app on Coolify, Dokku, or CapRover in under 90 seconds. That's the whole pitch, and for a solo developer or a five-person team it's a genuinely better experience than wiring up a CI pipeline by hand. The part none of these tools' landing pages mention is where that experience stops scaling — not vaguely, but at specific numbers: a RAM figure, a CPU percentage during deploys, and a very literal single point of failure sitting on the one box everything runs on.
One Daemon, Every Feature
Coolify, Dokku, CapRover, and Dokploy are usually compared feature-by-feature — build packs, preview environments, one-click databases — but they share one architectural decision underneath all of it: a single Docker daemon on a single server is the control plane, the build runner, and the runtime, all at once.
Dokku is the purest version of this. It's a set of Bash/Docker plugins on top of one host, with no built-in multi-server story at all — scaling means manually running more app instances on that same box, and autoscaling requires third-party plugins rather than anything native.
CapRover clusters via Docker Swarm, so multi-server is technically built in. But Swarm mode in this context has a specific reputation: it works until a node drops, and then the failure mode is quiet rather than loud — services can silently fail to reschedule instead of throwing an obvious error, which is worse for an on-call engineer than a hard crash.
Coolify supports a control-plane-plus-workers model — one server runs the dashboard, database, and Git webhook handling, while additional servers register as deploy targets. It's the most actively developed multi-server story of the three, but it currently ships with sharp edges. The UI blocks setting up a multi-server deployment on any application that has a volume mount attached, Swarm mode itself is still flagged experimental, and the API only accepts a single server_uuid per application with no route to add servers or repoint an app after creation.
Coolify's own team has said Kubernetes support isn't on the near-term roadmap; v5 is instead building a custom Docker-based scaling layer described as "like Swarm, but better" — which is another way of saying multi-node orchestration is still being built, not shipped.
Dokploy is the newest of the four and markets multi-server as more automated out of the box, built on the same Swarm foundation as CapRover.
The common thread: whichever tool you pick, the software making scheduling decisions is one daemon on one machine. Everything past that is either a manual workaround or a roadmap item.
Where the Ceiling Actually Sits
"Eventually you'll need more than one server" is true of almost any architecture and therefore useless on its own. The concrete version, from the platforms' own documentation, GitHub issue trackers, and community reports:
Idle overhead before you deploy anything. Coolify's own stack — PHP backend, Node frontend, Postgres, Traefik — runs roughly 500MB–1.2GB of RAM and 5–6% CPU at idle, depending on which report you read; Dokploy's leaner Go-based stack idles closer to 350MB and under 1% CPU. On a 2GB VPS, that overhead alone leaves roughly 800MB–1.2GB free — enough for one lightweight app, not a real multi-service stack. The commonly recommended floor is 4GB RAM / 2–4 vCPU, and even that "handles a surprising amount" only because Docker sets no per-container memory or CPU limit by default — meaning one misbehaving container can consume everything else on the box unless you explicitly cap it.
Traffic is not actually the first bottleneck — deploys are. It's tempting to frame the ceiling in requests per second, but Traefik (the reverse proxy Coolify and Dokploy both run) benchmarks in the 10,000–50,000 req/s range on modest hardware — comfortably past what the overwhelming majority of self-hosted, small-fleet apps will ever throw at it. The proxy isn't the wall.
The wall is CPU contention during a deploy. Building a Docker image is CPU-intensive, and because the build runs on the same daemon serving live traffic, a deploy can spike CPU past 300% on multi-core boxes and visibly slow down every other running app on the host. Worse, a rolling deploy briefly runs both the old and new container simultaneously, roughly doubling that service's RAM footprint for the overlap window. A server that's comfortably under load at steady-state can still fall over the moment someone pushes a deploy during a traffic peak — a materially different (and more common) failure than "too many requests."
Where the number actually falls. There's no single magic app count — it scales with VPS tier — but the community-recommended math is: total each service's realistic memory reservation, add the platform's own overhead (500MB–1.2GB depending on tool), add 20% headroom for the double-container window during deploys, and that's your floor. In practice that puts a 4GB/2vCPU box comfortably at 3–5 lightweight services before CPU-during-deploy contention becomes a visible, recurring problem — not a hard cap, but the point past which "just add another app" starts costing you deploy-time slowdowns on everything else.
The One Box Is the One Point of Failure
This is the sharpest edge, and it's structural rather than a bug any of these tools can patch away: the control plane — the dashboard, the deployment orchestrator, the reverse proxy terminating TLS for every app — runs on the same machine as the workloads it manages. Reboot or crash that box, and you don't lose one app; you lose the ability to see, manage, or route to any of them at once.
Docker Engine's own restart policies mean containers generally come back up automatically once the daemon restarts — that part works. What doesn't come back automatically is everything above the daemon: if the crash was disk pressure, a kernel panic, or a provider-side outage, the dashboard, the Git webhook receiver, and the reverse proxy are down until a human intervenes, and there's no second instance anywhere to fail over to. CapRover's Swarm mode is the partial exception — a worker node can keep serving already-scheduled containers if it isn't the one that went down — but losing the Swarm manager still takes management access offline until it's restored, and Swarm's failure signals for a lost node are, again, the "quiet" kind rather than an alert firing.
None of this is a criticism of the engineering — it's what "single Docker daemon" structurally means. There is exactly one thing making scheduling decisions, and when it's unavailable, decision-making for the whole fleet is unavailable with it.
What a Fleet-Managed Cluster Actually Buys
This is the specific gap Cluster API–based platforms close, and it's worth being precise about the mechanism rather than waving at "Kubernetes is more scalable."
Cluster API represents every machine as a declarative object — a Machine, grouped into a MachineDeployment — instead of one daemon's live, in-memory opinion of what's running where. That declarative layer is what makes the next two properties possible:
- Automated node replacement. A
MachineHealthCheckcontroller watches for nodes that goNotReadyorUnknownand, on failing the check, marks that node for remediation. Its owning controller then provisions a replacement automatically — typically within minutes, with no SSH session and no human paging themselves at 2 a.m. This is the automated version of the exact manual recovery step Coolify and CapRover operators report doing by hand today: log in, find out what died, restart it. - Fleet-wide rolling upgrades. Upgrading a Cluster API–managed fleet rolls new machines in and old ones out node by node, with health checks paused during the rollout window specifically so the upgrade itself doesn't get flagged as a failure and trigger conflicting remediation. That's a materially different operation than manually updating one Docker host and hoping nothing that was running on it needed to stay up during the process.
And because Cluster API's machine abstraction is provider-agnostic — Metal3 and Canonical MaaS both implement it for bare metal, the same shape used for cloud VMs — adding a second physical machine to the fleet is a config change to an existing declarative object, not new orchestration logic bolted onto a single-daemon tool that was never designed to coordinate more than one host in the first place.
Where This Leaves the Decision
None of this makes Coolify, Dokku, or CapRover the wrong choice — for a single app, a side project, or a small team that will never need more capacity than one well-specced box provides, the git-push simplicity is a genuine win and multi-node orchestration would be pure overhead. The wall described here is real, but it's also a wall a lot of workloads will simply never hit.
The team that does hit it is the one this post is actually for: enough services that the 4GB box is visibly straining during every deploy, or a reboot that just took down every app at once instead of one. At that point, the fix isn't a bigger VPS — the deploy-time CPU contention scales with app count, not box size — it's a scheduling layer that was built to coordinate more than one machine from day one.
Bex.co is the open-source, AI-native Render alternative built on exactly that model — Cluster API managing a fleet of machines you own, with MachineHealthCheck-driven node replacement and rolling upgrades already wired in, not a roadmap item. It's still a git push to deploy. Star the repo on GitHub or deploy your first app today.



