Skip to main content

Kubero Nailed Kubernetes-Native PaaS — But Somebody Still Has to Build the Cluster

13 min readDora NodaDora Noda
Share

One-click deploy to your own server feels like magic until you need a second server. Coolify spins up WordPress from a git repo in two minutes on a single Hetzner CX22. Dokploy does the same with a slick dashboard and 26,000 GitHub stars pre-1.0. You push, it builds, it gives you HTTPS — done.

Now add a second machine. Where does the workload land? Who heals the node that dies at 3 a.m.? Who rolls the upgrade without dropping traffic? That is exactly where the Docker-on-one-box abstraction ends and where Kubero — the free, self-hosted PaaS that runs on Kubernetes instead of instead of Kubernetes — starts to make sense. And it is also where Kubero stops and hands you the bill for the cluster itself.

This post is the feature-by-feature receipt you need before you pick a box or a fleet.

Quick verdict — the table most teams skip until they outgrow the box:

CapabilityCoolify / Dokploy / CapRover (single-box)Kubero (K8s-native dashboard)bex / CAPI fleet (dashboard + machine lifecycle)
RuntimeDocker directly on one VPSTwo pods (kubero-ui + operator) on your K8s; apps as K8s Deployments without writing HelmSame as Kubero, but K8s itself is declared via Cluster API
Multi-node scheduling⚠️ Partial — manual Swarm or "add server" bolt-on; no real scheduler✅ Kubernetes scheduler with bin-packing, affinity, rolling updates✅ Same + node provisioning declared in HetznerMachineTemplate
High availability & autoscaling❌ Single point of failure; scale = bigger box✅ ReplicaSets, PDBs, HPA — if your cluster has the nodes✅ + nodes appear automatically as Machine resources
GitOps & review appsBasic auto-deploy on push; review envs manual✅ Pipelines (4 phases: review/test/stage/prod), automatic review apps per PR✅ Same + preview envs as ephemeral CAPI workload clusters
App catalogDozens of one-click services✅ 160+ templates (WordPress, Grafana, etc.)Same templates, plus fleet-wide operators
Add-ons (Postgres, Redis)Docker containers on same host; backups are your jobAdd-ons via Helm (groundhog2k charts) — great to start, not HA by defaultBring your own managed Postgres (Neon, Supabase, RDS) or self-host on fleet — bex deliberately has no managed DB
Operations the platform hidesThe VPS itselfNot hidden — Kubero docs: "What Kubero won't do for you — Manage your Kubernetes cluster"Machine lifecycle, upgrades, self-healing (MachineHealthCheck), etcd backup

If that table already changed your mental model from "which dashboard is prettier" to "who owns the machines under the dashboard," the rest of the post fills in why.


The Single-Box Ceiling: Why the Second Server Breaks the Abstraction

Coolify, Dokploy, CapRover, and their cousins Mooring and Docklift solved a real problem: make Docker self-hosting as easy as Heroku's git push. You get:

  • Git-triggered builds with Nixpacks/buildpacks
  • Automatic TLS via Let's Encrypt
  • A UI for env vars, volumes, and cron jobs
  • Backups and monitoring dashboards

For one app on one box, they are excellent. The Coolify team shipped an MCP server in v4.0; Dokploy iterates fast toward 1.0; CapRover has been stable for years. Teams that moved off Render or Railway for cost reasons often land here first and save immediately.

The ceiling appears the moment you stop fitting on one machine:

No fleet-wide node provisioning. There is no declarative Machine you apply and watch a node appear. Adding a server in Coolify/Dokploy is a manual SSH-and-join flow that lives outside the dashboard's state. Compare that to Cluster API, where kubectl apply -f machine.yaml is the only way a node comes to exist and the controller reconciles drift.

No declarative machine lifecycle. Single-box tools know about containers, not machines. They do not expose MachineHealthCheck that replaces a dead node, KubeadmControlPlane rolling upgrades, or provider-specific templates like HetznerMachineTemplate with server type, location, and image pinned as code. When a node fails, a human tickets it.

No real scheduler. Docker on one host is first-come, first-served. Kubernetes scheduling is bin-packing with topology, affinity, and eviction policy. The difference is invisible at 2 apps and decisive at 20 apps per tenant across 6 nodes, where placement determines per-tenant unit cost.

Rollouts that fight the platform. Saylor's team building Sailbox wrote it plainly after trying the single-box trio: "We used Coolify, Dokploy, CapRover — they're great ... but when we needed real health checks, graceful rolling updates, autoscaling, or the ability to grow from one node to three, we found ourselves fighting the abstraction instead of shipping." Miabi's comparison table makes the same point with checkboxes: multi-node deployments are ✅ only for Kubernetes-native platforms; for the Docker dashboards they are partial at best.

This is not a critique of those projects. They optimize for "manage the box you already have." A fleet optimizes for "manage the next box you haven't bought yet." The gap between those optimizations is exactly the surface Kubero fills — and the surface Kubero intentionally leaves empty.

What Kubero Gets Right: PaaS on Kubernetes, Not Instead of Kubernetes

Kubero [pronounced Kube Hero] is a self-hosted PaaS that allows any developer to deploy on Kubernetes without learning Helm. The pitch is deliberately narrow, and that narrowness is why it works.

Two pods and your etcd

Kubero runs as two containers on any Kubernetes instance: kubero-ui and an operator (Operator SDK + Helm under the hood). Every app you create becomes a standard Kubernetes Deployment/Service/Ingress; all platform state lives in your cluster's etcd — no extra Postgres to operate. Bring your own cluster (EKS, GKE, k3s, Kind for dev) or let kubero install scaffold one on GKE, Scaleway, DigitalOcean, Linode, or Kind. The architecture diagram is almost anti-climactic: UI → Operator → Kubernetes API → your nodes. That is the point.

The same minimalism shows in git support. Kubero speaks GitHub, GitLab, Bitbucket, Gitea/Forgejo, and Gogs, hosted or self-hosted. Create a pipeline with up to four phases (review, test, stage, production), connect the repo, and pushes to a branch or tag trigger a build in the cluster's own build pods. No external CI required, though you can call its API and CLI from an external pipeline if you prefer.

GitOps review apps that actually clean up

One feature separates a demo PaaS from a platform a team can live in: review apps. Kubero's GitOps review apps automatically build, start, and clean up a disposable environment when a pull request opens or closes. That lifecycle — not just "deploy this branch somewhere" but "delete it when the PR merges" — is exactly what preview environments need and what single-box tools tend to implement as a manual "deploy this branch to another container on the same host." On Kubero the review app is a set of K8s resources in its own namespace, with its own add-ons and routes, governed by the same RBAC and quotas as production.

160 templates and Docker without Helm

The catalog matters for adoption. Kubero ships 160+ app templates (WordPress, Grafana, Bitwarden, Uptime Kuma, Trilium and counting) that deploy without writing a Helm chart. The same abstraction applies to your own code: anything that can be packaged in a single container — Go, Python, Node, PHP/Laravel, Ruby/Rails, Rust/Rocket, static HTML — runs via Dockerfile or buildpack with no chart authoring. The platform renders the Helm release for you, which is why its docs can claim "deploy Docker containers on Kubernetes without needing Helm charts" without meaning "without Helm existing."

Add-ons with honest scoping

Add-ons (PostgreSQL, Redis, MySQL, MongoDB, RabbitMQ, CouchDB, plus CloudNativePG, Crunchy, MinIO, ClickHouse and others on recent charts) deploy alongside your apps as Helm releases via maintainers like groundhog2k. The asterisk matters and Kubero marks it clearly: built-in add-ons are "not HA ready but great to get you started as fast as possible." Bitnami charts are marked deprecated due to Broadcom's image registry changes. This is not a managed database promise — it is a "cluster-scoped dev database" that you will replace with a real Postgres before traffic grows. For a PaaS that brands itself as Heroku-on-your-K8s, that honesty about HA is load-bearing: it is exactly why bex's non-goal of "we don't run your Postgres" is not a gap to close but a boundary to respect — bring Neon, Supabase, RDS, or self-host CloudNativePG on the same fleet, but own the data layer separately from the app scheduler.

Other table stakes are all present and scoped the same way: integrated metrics and logs in the UI, vulnerability scans, safe restarts, a web console, cronjobs, multi-tenancy, SSO via GitHub/OAuth2, basic auth per app, and Discord/Slack/webhook notifications. Nothing here is category-defining on its own. Together with the scheduler underneath, they are what "PaaS on Kubernetes" means in practice: every Heroku-like verb you invoke resolves to a Kubernetes API call a platform engineer can kubectl get and audit.

The Cluster You Still Have to Build

Here is the line that should be in bold on any Kubero evaluation doc:

What Kubero won't do for you — Manage your Kubernetes cluster.

That is a direct quote from the project's own README and documentation forks, and it appears under its own heading for a reason. Kubero is a dashboard and an operator for workloads, not a provisioner for machines.

In concrete terms, you still own:

  • Provisioning the cluster. Control-plane HA, etcd sizing, CNI, storage class, ingress controller, and the node pool all exist before Kubero arrives. kubero install helps on five clouds and locally via Kind, but it does not provision bare-metal Hetzner nodes via Hetzner Robot or Cloud APIs, does not write HetznerCluster or HetznerMachineTemplate, and does not reconcile machine count against a Git-stored desired state.

  • Machine lifecycle and upgrades. Rolling from Kubernetes v1.32 to v1.33, rotating images, or replacing a failed worker is outside Kubero's controller. There is no MachineHealthCheck remediating a dead host within nodeStartupTimeout, no KubeadmControlPlane version skew guard, no declarative scale-out when pending pods sit unscheduled.

  • Day-2 operations. Etcd snapshots and restore drills, certificate rotation, CNI upgrades, CSI driver compatibility (for example Volume Group Snapshots graduating to GA in v1.36), node kubelet flag migrations like kube-proxy ipvs deprecation — all remain operator work.

None of this is hidden; the project's install matrix makes it explicit. "Bring your own cluster" is the prerequisite, not the footnote. Teams that already run a well-operated EKS/GKE cluster or a maintained k3s fleet can adopt Kubero and get PaaS value on day one. Teams that adopt Kubero to avoid operating Kubernetes have adopted the dependency they were trying to dodge.

Filtering signal from noise here matters. Kubero's own template catalog and add-ons are excellent. Ownkube (managed PaaS in your AWS account on k3s/EKS with agent-based ops) and Agnost (open-source PaaS with its own scheduler) live in the same Kubernetes-native bucket and make the same structural claim: real PaaS needs a real scheduler. The category to keep distinct is "infrastructure that gives you a dashboard" versus "infrastructure that gives you the machines the dashboard runs on." Kubero is firmly the former. That is not a flaw. It is a scope choice that determines what your on-call owns at 3 a.m.

Closing the Loop: From Dashboard to Fleet

The gap Kubero leaves is exactly the seam a Cluster API fleet is built to close. If Kubero answers "how do I get Heroku-like verbs on the Kubernetes I have," a CAPI-managed platform answers "how does the Kubernetes I have come to exist, heal, and upgrade itself from Git?"

In bex's case that means:

  • Declarative machines. A Hetzner box is not SSH'd into existence. It is a HetznerMachineTemplate with serverType, location, image, and placement pinned as code. Change the template, commit, and the fleet reconciles.

  • Self-healing, not just self-hosting. MachineHealthCheck watches for a node whose kubelet vanished, cordons it, and triggers remediation without a human pager. Workloads reschedule via the scheduler Kubero already relies on — but now the scheduler has somewhere to reschedule to because the machine controller can replace the member, not just mark it missing.

  • Rolling upgrades as a CR, not a weekend. Control-plane upgrades via KubeadmControlPlane and worker rolling upgrades via MachineDeployment turn "drain, replace, verify" into a reconciled rollout rather than a runbook.

  • Same git-push UX. bex.yml in the repo root, bex deploy from an agent or a human, preview environments per PR, custom domains with TLS, logs and metrics — the surface Kubero gets right, kept as a Render-compatible API so the same workflows survive the migration from Render, Fly.io, or a single-box dashboard.

This is not a "use Kubero or bex" framing. A team with a healthy existing cluster should try Kubero today; it is free, it demos well at demo.kubero.dev, and its two-pod install is reversible. A team that wants that same "git-push gets HTTPS" experience without staffing the cluster layer separately wants the fleet underneath the dashboard — which is the one layer Kubero deliberately refuses to be.

Choose accordingly:

  • One server, no interest in Kubernetes ops → Coolify/Dokploy. Fastest time to "it's live," lowest cognitive overhead, and honest about the single-box trade-off.

  • You already operate healthy Kubernetes and want PaaS verbs → Kubero. You keep your cluster, you add a dashboard and an operator, you gain review apps and templates on day one.

  • You want PaaS verbs without owning the cluster lifecycle → a CAPI fleet (bex). You keep the git-push experience, you add declarative machines that provision, heal, and upgrade from Git.

The wrong choice is not picking the "less powerful" option. It is picking a dashboard while assuming someone else is minding the machines under it. Kubero's docs do not make that assumption. Your evaluation shouldn't either.

Sources & Further Reading

  • Kubero README and docs — kubero-dev/kubero on GitHub, https://www.kubero.dev/docs/ — two-pod architecture, 160+ templates, pipeline phases, add-ons, and "What Kubero won't do for you"
  • Sailbox (K3s-based PaaS) — "We used Coolify, Dokploy, CapRover … fighting the abstraction instead of shipping" on single-box limits
  • Miabi and awesome-paas comparisons — multi-node deployment checkboxes (Kubero ✅, Coolify/Dokploy partial)
  • Zattera.dev — "Web panels on Docker … no real multi-server orchestration" framing
  • Coolify v4.0 release notes (MCP server), Dokploy 26k stars pre-1.0, CapRover stability — single-box field context from blog-TODO premise

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. 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