Skip to main content

Kamaji Turns Kubernetes Control Planes Into Pods — But It Only Cuts the Control-Plane Bill, Not the Worker-Node One

9 min readDora NodaDora Noda
Share
On this page

Every Cluster-API-managed fleet that provisions a tenant cluster the ordinary way — kubeadm, one or three control-plane nodes, etcd stacked on top — pays for that control plane whether or not the tenant is using it. A production-grade setup needs three of those nodes for etcd quorum. Fifty tenants means 150 machines doing nothing but keeping kube-apiserver and etcd alive.

Kamaji's answer is to stop dedicating machines to that job at all. It runs each tenant's control plane — kube-apiserver, kube-controller-manager, kube-scheduler — as ordinary pods inside a shared management cluster, and it plugs directly into Cluster API as a control-plane provider, a drop-in alternative to KubeadmControlPlane. That much matches the pitch.

What it doesn't do is what a reader might assume from the vCluster comparisons already floating around this space: Kamaji doesn't shrink a tenant's total footprint. It removes exactly one cost line — the dedicated control-plane machine — and leaves the worker nodes untouched, because a Kamaji tenant still gets a fully dedicated node pool for its actual workloads. That's a narrower claim than "cheaper multi-tenancy," and it leaves two questions worth answering with real numbers: how much does the control-plane line actually shrink, and does a shared-kernel control plane meet the isolation bar a self-hosted PaaS is supposed to guarantee even when the workers stay dedicated.

How Kamaji actually plugs into Cluster API

KamajiControlPlane is a Kubernetes custom resource that speaks the same clusterctl contract as KubeadmControlPlane — a Cluster API Cluster object points at it the same way, and the rest of the CAPI machinery (infrastructure provider, bootstrap provider, MachineDeployments for workers) doesn't know or care that the control plane on the other end is pods instead of VMs.

Each tenant's TenantControlPlane resource stands up kube-apiserver, kube-controller-manager, and kube-scheduler as regular Deployments in the management cluster, at documented default resource requests: the API server at 250m CPU / 512Mi RAM, the controller-manager at 125m / 256Mi, the scheduler at 125m / 256Mi — roughly 0.5 vCPU and 1GiB of RAM per tenant control plane. That number matters later; it's the thing a "just run it as pods" pitch has to actually pay for somewhere.

The datastore is decoupled from the control-plane pods by design. kamaji-etcd runs as its own multi-tenant etcd deployment — shared across many tenants via per-tenant keyspaces and RBAC, or dedicated one-to-one where a tenant needs its own — so a fleet isn't standing up a fresh three-node etcd cluster every time it provisions a tenant. Worker nodes bootstrap exactly the way they already do on any CAPI fleet, through the normal bootstrap providers, pointed at the tenant control plane's kube-apiserver Service (LoadBalancer, NodePort, or ClusterIP) instead of a VM's IP.

Upgrading a tenant means swapping pods, not draining and reprovisioning machines. A Kubernetes version bump on a TenantControlPlane runs as a documented blue/green rollout — maxSurge: 100%, maxUnavailable: 0 — with the new control-plane pods up and serving before the old ones terminate, landing the whole cutover in about 10 seconds instead of a multi-minute node-by-node upgrade.

This isn't a lab toy. OVHcloud runs its managed Kubernetes offering on Kamaji, Rackspace Spot uses it under its auctioned bare-metal Kubernetes marketplace, and NVIDIA picked it for the DOCA platform — three different production shapes (hyperscaler-adjacent managed K8s, a spot market, and vendor infrastructure software) that all chose the same underlying mechanism.

The worked cost model — and where it stops paying off

Take the same Hetzner baseline a Cluster-API-managed fleet already budgets against: a CX22 (2 vCPU / 4GB RAM, roughly €4.59/month). A tenant control plane's ~0.5 vCPU / 1GiB default footprint means a CX22 can host about 3 tenant control-plane pods with headroom left for the node's own OS and kubelet overhead. A shared kamaji-etcd deployment sized for HA — 3 CX22 nodes — costs a fixed €13.77/month regardless of how many tenants sit behind it, since it's one shared datastore, not one per tenant.

Put those two pieces together against two baselines for "dedicate a machine per tenant instead": a single non-HA control-plane node (€4.59/tenant/month, with etcd as a single point of failure) and a proper 3-node HA kubeadm control plane (€13.77/tenant/month). Here's what a fleet actually pays per tenant, at three sizes:

TenantsKamaji: pod nodesKamaji: shared etcdKamaji totalKamaji €/tenantDedicated (non-HA)Dedicated (HA)
52 × CX22 = €9.18€13.77€22.95€4.59€4.59€13.77
104 × CX22 = €18.36€13.77€32.13€3.21€4.59€13.77
5017 × CX22 = €78.03€13.77€91.80€1.84€4.59€13.77

Against the HA baseline every row is a clear win — 66% cheaper at 5 tenants, 87% cheaper at 50. Against the cheaper but riskier non-HA baseline, the picture is tighter: at 5 tenants Kamaji's fixed shared-etcd overhead means it lands at the same €4.59/tenant, not below it.

The crossover sits around 5 tenants — solve €13.77 + ⌈N/3⌉ × €4.59 < N × €4.59 and the fixed cost stops dominating once N passes roughly 5. Below that, a single dedicated non-HA control-plane machine can be just as cheap as paying for a shared HA etcd deployment that isn't yet amortized across enough tenants. Kamaji's savings are real, but they're a function of fleet size, not a flat multiplier — the same shape of caveat that applies to any shared-fixed-cost infrastructure, and worth stating rather than only showing the flattering row.

What doesn't change: the worker line

None of that touches the actual workload. A Kamaji tenant's Kubernetes objects — its Deployments, its Pods — still run on a worker node pool dedicated to that tenant alone, exactly as they would in the kubeadm-per-tenant baseline. That's a deliberate design choice, not an oversight: Kamaji's isolation model keeps each tenant's data plane on its own machines specifically so a noisy or compromised tenant's workload never lands on hardware another tenant is also running on.

That's also exactly where Kamaji and vCluster answer a different question. vCluster virtualizes the control plane and schedules tenant workloads onto a shared node pool alongside other tenants — the model this blog already walked through in detail, where 50 tenants sharing a pool of CCX23 nodes cut the control-plane line by roughly 3.4x over one dedicated machine per tenant. Kamaji gets a comparable (in this cost model, larger) reduction on that same control-plane line, but the worker-node bill next to it is unchanged from the dedicated-machine baseline in both the 5-tenant and 50-tenant rows above:

ModelControl-plane costWorker-node costIsolation boundary
Dedicated machine per tenantHighest (own HA control plane)Highest (own dedicated workers)Full — own kernel, own compute, no shared component
KamajiLow, shared management clusterSame as dedicated — own worker node pool per tenantDedicated data plane; shared-kernel control plane
vClusterLow, shared host clusterLow, shared host node poolOwn API server/RBAC/CRDs; shared kernel and compute throughout

Kamaji only ever collapses one row of that table. Anyone reading "control planes as pods" as "this is vCluster's savings with a different label" is answering the wrong question.

The isolation question that actually decides it

Kamaji's marketing calls its worker-node model "hard multi-tenancy," and on the data plane that's accurate — no other tenant's workload ever gets scheduled onto a machine dedicated to a different tenant. But the control-plane pods themselves don't get that same guarantee. A TenantControlPlane's kube-apiserver pod runs on a management-cluster node that, by default, other tenants' control-plane pods share too — same kernel, same node-level cgroup and network stack, the same class of shared-kernel boundary any two pods on the same Kubernetes node have. The separation between tenants at that layer is namespaces, RBAC, and resource quotas — a software boundary Kamaji itself enforces well, but not a hardware one, and not the same guarantee the worker nodes get.

That's not a defect; it's the actual shape of the tradeoff, and it resolves the isolation question cleanly rather than needing a judgment call. Kamaji fits a tenant whose isolation requirement is about the data plane — "my workloads and data never run on a machine another tenant's workloads touch" — which is what most compliance and noisy-neighbor concerns are actually about. It does not satisfy a stricter requirement that a tenant's control plane never shares a kernel with anyone else's.

A fleet that needs that stricter bar has two options, and both cost something: pin each tenant's control-plane pods to their own node via anti-affinity (which claws back most of the shared-capacity savings computed above, since you're back to paying for near-dedicated control-plane capacity per tenant), or skip Kamaji for that tenant and provision a genuinely separate control plane.

Where this actually lands for a fleet like bex

The decision rule falls out of the crossover point already computed, not out of a vibe: if a fleet is already dedicating worker nodes per tenant for compliance or isolation reasons, and has more than about 5 such tenants, swapping a hand-rolled kubeadm HA control plane for KamajiControlPlane is close to a free win — it removes real control-plane machine cost without weakening the guarantee those tenants are actually paying for, since that guarantee was always about the workers. Below roughly 5 tenants in that tier, the shared-etcd fixed cost isn't amortized yet, and a single dedicated control-plane machine can be just as cheap.

For the more common case on a self-hosted PaaS — many small or bursty tenants who don't need dedicated worker hardware at all — Kamaji isn't the right comparison. The shared-node-pool model this blog already covered is, since that's the one that also collapses the worker-node line Kamaji deliberately leaves alone.


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