Skip to main content

Boring Infrastructure on Purpose: What Uncloud's Control-Plane-Free Clustering Keeps — and What It Quietly Gives Up

12 min readDora NodaDora Noda
Share
On this page

The most honest sentence on Uncloud's homepage is not the pitch — it is the warning: "Nothing scales or reschedules on its own. It's predictable, boring infrastructure for your peace of mind." Uncloud, the open-source tool that turns a handful of Docker hosts into a multi-machine cluster with no control plane and no quorum, states its biggest limitation as a feature. And the numbers say the framing lands: 5,200+ GitHub stars, 21,000+ downloads, two Hacker News front pages — plus the endorsement that matters most, a production one: Radboud University is moving 300+ websites onto a dozen Uncloud machines, championed by CoreDNS creator and ex-Google SRE Miek Gieben.

So here is the concrete trade-off, stated before anything else, because this post is a buying decision disguised as an architecture review:

CapabilityUncloud (no control plane)Control-plane orchestrator (Swarm / Nomad / Kubernetes)
Dead container on a live machineRestarted (health checks + automatic restarts)Restarted, then rescheduled if the machine itself is the problem
Dead machineReplicas stay dead until you redeploy; survivors keep servingReplicas rescheduled onto healthy machines automatically
New machine joinsOne SSH command (uc machine add), no quorum mathJoin procedure + quorum/etcd capacity planning
Desired stateImperative: the last uc deploy plan is the truthDeclarative: controllers reconcile drift continuously
Image distributionDirect layer push to machines, no registry to runRegistry required (or an equivalent to operate)
Machine-readable state APICLI + SSH; no uniform API to watch and reconcile againstA real API (Kubernetes API, Nomad API) that tooling and agents speak
Upgrade burdenUpgrade Docker, WireGuard, Caddy — familiar standalone piecesControl-plane upgrades, etcd, API deprecations, CRD migrations
DebuggabilitySSH in, use plain Docker and Linux toolsDistributed-system debugging: Raft, informers, scheduler decisions

Bottom line: Uncloud deletes an entire category of 3am pages (quorum loss, failed control-plane upgrades, etcd surgery) and replaces them with a single, legible failure mode — a dead machine stays dead until an operator intervenes. Every section below prices that exchange from a different direction.

How it works in one page

Uncloud's architecture is three boring technologies composed well. Each machine runs Docker plus a small uncloudd daemon. uc machine init over SSH installs both, creates a WireGuard mesh (each machine gets an address like 10.210.0.1/24), and deploys Caddy as a global service for ingress. uc machine add repeats the SSH dance for each new box — no bootstrap tokens, no join quorum, no etcd cluster to size. State is shared peer-to-peer between daemons, so the cluster keeps functioning when individual machines drop out. There is no leader to elect and no majority to lose.

Deployments are the part that converts skeptics. You keep your standard compose.yaml — no new spec — and run uc deploy. Uncloud builds the images, pushes layers directly to the target machines (transferring only missing layers) through its Unregistry mechanism, so there is no registry to provision, pay for, or rotate credentials against. Then it prints a Terraform-style plan — exactly which containers on which machines get replaced, start-first, with health checks gating each step — and waits for confirmation. Rollback is the same motion in reverse: the previous plan, re-applied.

Networking follows the same "compose, but multi-host" logic. Services reach each other by name across machines over the WireGuard mesh, so a database never needs a public port. Publishing to the internet is an Uncloud compose extension, x-ports (app.example.com:8080/https), and the Caddy service reconfigures itself on every container change with certificates issued and renewed automatically. Managed DNS (*.uncld.dev) covers the demo-to-first-deploy path. Scaling is uc scale web 3 plus a plan to confirm; storage is named Docker volumes on the machine that runs the container.

None of this is novel technology. That is the entire point, and the project's own users say it best: "It's all built on top of simple and familiar technologies… This gives me confidence that if something goes wrong, I will be able to debug myself. I cannot say the same for SwarmKit or etcd." When the abstraction leaks — and every abstraction leaks — the leak looks like Docker, WireGuard, and Caddy, three tools your team already knows.

What you give up: the dead-machine walkthrough

Take the typical fleet this tool targets: three machines, a stateless web service at three replicas spread across them, one Postgres as a single replica with a named volume. Now kill machine two at 3am. Here is what happens, precisely:

  1. The web replica on machine two is gone. The two survivors keep serving — Caddy stops routing to the dead box — so nobody's site goes down at 3:05am.
  2. Nothing re-creates the lost replica. There is no controller watching desired-versus-actual state, because there is no control plane to run it. Your fleet is now at two-thirds capacity indefinitely.
  3. The Postgres is fine if it lived on machine one or three. If it lived on machine two, your app is down until a human restores the volume's host or re-provisions from backup — same as it would be on a single Docker host, because without rescheduling, stateful failover simply does not exist.
  4. Morning comes, you provision a replacement, run uc machine add, redeploy, and capacity is whole again. Total data-plane automation involved: zero. Total mystery involved: also zero.

Whether this is acceptable is a capacity-planning question, not an architecture question. If your three replicas were sized so that two carry peak load, the dead machine is a degraded-capacity incident you fix during business hours. If three replicas were the minimum for peak, it is an outage with extra steps. Uncloud does not save you from that math; it just refuses to hide it behind a scheduler.

Compare honestly with the alternative: a reconciling orchestrator would have rescheduled the replica in minutes — and would also have handed you etcd quorum incidents, control-plane version skew, and upgrade windows as the price. One failure mode is "degraded until a human acts"; the other is "self-healing, plus a second, weirder failure domain that pages you for reasons involving Raft." Small teams routinely, rationally prefer the first — which is exactly why Radboud's shape (a dozen machines, hundreds of mostly-quiet sites, huge headroom per box) fits Uncloud so well. The headroom is the high-availability strategy.

Two more things never arrive without a control plane, and both matter sooner than the dead-machine case. First, there is no autoscaling substrate: no metrics pipeline feeding replica decisions, no cluster-autoscaler equivalent adding machines. uc scale is a human (or a cron job you wrote and now own) typing a number. Second, there is no uniform state API. uc plus SSH is scriptable, but scripts parse CLI output; there is nothing to watch, no resource version to retry against, no controller to extend. That gap is invisible while humans operate the fleet and becomes the whole story once something else does — which is the next section's subject.

The neighborhood map

Uncloud sits in a crowded gap between "one box" and "real orchestrator," and each neighbor wins a different argument:

  • Kamal (37signals) is the imperative-push purist: kamal deploy from your workstation rolls containers across servers behind Traefik, with accessories for databases. But Kamal has no cluster state at all — no cross-host service discovery, no mesh network, no shared view of what runs where. Choose Kamal when deploys are the only multi-machine problem you have; choose Uncloud when containers on different machines need to find and trust each other.
  • Dokku / Coolify / Dokploy are single-box PaaSes first. Dokku is the original mini-Heroku; Coolify and Dokploy add nicer git-push workflows and some multi-server reach, but the mental model is still "a server that hosts apps," managed through a central control server that is itself a single point of failure and care. Choose them when one big box (or a primary plus spares) fits; choose Uncloud when the workload genuinely sprawls across machines that should be peers, not pets of a dashboard.
  • Docker Swarm is the ghost this project is exorcising. Swarm mode still ships in Docker Engine and Mirantis extended support to 2030, but it has received no meaningful new features in years — maintenance, not momentum. Uncloud's own docs name Swarm migrants explicitly, and the pitch writes itself: keep the Compose format, drop the manager quorum, gain WireGuard networking, registry-free pushes, and managed ingress. If you are still on Swarm, this is your off-ramp with the smallest retraining bill.
  • Nomad lost the "boring and free" crown the hard way: HashiCorp's 2023 license switch to BSL, followed by the IBM acquisition, ended the era where Nomad was the safe default for teams avoiding Kubernetes on principle. Nomad remains technically excellent, but its governance story now requires a trust calculation Uncloud — Apache-2.0-licensed, small enough to fork or fully understand — does not.
  • Kubernetes / Cluster API is what you graduate to, not what you start with. Declarative reconciliation, autoscaling, policy engines, a device-plugin/DRA path to GPUs, and an API ecosystem every tool speaks — the table at the top concedes all of it. The price is a control plane you operate forever: etcd, upgrades, API deprecations, CRD migrations, YAML sprawl. Adopt it when the dead-machine math stops working — when replicas must come back without a human, when capacity must move itself, when tenants need isolation stronger than a shared Docker daemon.

Why agents change the answer

The TODO item behind this post insists the verdict shifts once AI agents, not humans, are the primary operators — and it does, but against Uncloud, not for it. An agent operates infrastructure the way a controller does: read state, compare with intent, act, retry on conflict, watch for convergence. The Kubernetes API is, almost by accident, the best agent interface in infrastructure: uniform resources, versions and retries, server-side apply, watches instead of polling, RBAC scoping what the agent may touch. A platform that wants MCP-driven agent operators gets all of that nearly free.

Against that bar, uc over SSH is a handicap — every operation is a fresh imperative command with no transaction, no watch, no structured state to reconcile against. Scripting a deploy agent on top is possible; giving it the convergence guarantees that make agents trustworthy is not, because there is nothing to converge to.

Steel-man the other side before dismissing it, because it is genuinely strong. Agents are prolific producers of confident, slightly-wrong actions, and the blast radius of a wrong action scales with the system's power: a confused agent with cluster-admin can delete namespaces faster than any human. Uncloud's imperative, plan-confirm model — every mutation previewed as a diff, applied only on confirmation — is arguably the safer agent substrate today: small action space, legible consequences, debuggable with tools the agent's supervisor already understands. And "SSH in and run Docker commands" is within every coding agent's existing skill set, while "write a correct operator" is not.

Both halves are true, which is why the honest verdict is staged, not absolute: Uncloud is a fine substrate for agents that deploy apps, and a poor one for agents that operate fleets. The moment the agent's job description includes "keep desired and actual state converged while machines come and go," it needs the control plane's API — you cannot reconcile against a system that keeps no desired state. A human typing y at a plan prompt is a feature; an agent fleet needing convergence is a customer for reconciliation.

Verdict: pick it, then know the exits

Pick Uncloud when the shape matches: a small team, two to a dozen machines (cloud VMs, bare metal, or mixed), mostly-stateless services plus a database or two, enough headroom that a dead box is a degraded day rather than an outage, and deploys a few times a day rather than a few times an hour. That describes an enormous number of real production systems, including university hosting fleets and agency client farms — the workloads Kubernetes was never cost-justified for and Swarm served while it still moved.

Start planning the exit when any of these turn true: replicas must return without a human (you need rescheduling), capacity must move itself (you need autoscaling), tenants need boundaries stronger than shared-daemon trust (you need real multi-tenancy), workloads need GPUs with topology-aware placement (you need DRA-class scheduling), or operators — human or agent — need a state API rather than a CLI. Each is a control-plane feature wearing a requirements costume. Until then, boring is a compliment, and Uncloud's bet is that for most fleets, "until then" is years.


Sources

  • Uncloud homepage: positioning, traction (5.2K+ stars, 21K+ downloads, 2x HN front page), Radboud University 300+ sites quote, "nothing scales or reschedules on its own" — uncloud.run
  • Uncloud docs overview: architecture, Swarm-migration positioning, health checks and automatic restarts, use cases — uncloud.run/docs
  • Unregistry (registry-free image distribution) — github.com/psviderski/unregistry
  • Haloy 2026 comparison: "Uncloud fills the gap between Docker Compose and Kubernetes for teams that need multi-host orchestration" — haloy.dev
  • Docker Swarm status: maintenance updates only, SwarmKit commits through late 2025; Mirantis support commitment through 2030 — dev.to reality check, Mirantis doubles down
  • Kamal: 37signals' container deploy tool, deploy.yml workflow, Docker + Traefik rolling deploys — kamal-deploy.org
  • HashiCorp BSL license change (2023) and IBM acquisition close (2025) as Nomad governance context — hashicorp.com, IBM acquisition announcement

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Uncloud proves how far boring primitives go; when your fleet needs the reconciling half — declarative state, self-healing, an API your agents can operate — that is the half bex is built on. Star the repo on GitHub or deploy your first app today.

Related articles

Give your agents a chain backend

Autonomous agents hit RPC endpoints very differently than people do. See what bex router handles on their behalf.

Read the agents guide