Multi-Tenancy Patterns and Which One Is Correct for You
veröffentlicht am 06.08.2026 von Dominik Heilbock
One core topic in Platform Engineering is multi tenancy. Natively Kubernetes supports this either by dedicated namespaces or dedicated clusters per tenant. Both come with advantages and disadvantages and need to be selected based on the use-case at hand. However they sit at two ends of tenant isolation. With this article we are trying to shed some light on mechanisms for tenant isolation, that sit between those opposites. All of these models is really a story about which layer of the stack people have chosen to bolt tenancy onto, and what they gave up to do it, as Kubernetes has no built in concepts of "tenancy". Therefore, the following 5 patterns will be discussed: Namespaces, separate clusters, vCluster, Capsule and KCP.
Namespaces
A Namespace is a a mechanism for grouping resources within a single cluster. You can add some isolation mechanisms and limits via RoleBindings that scope a role to a specific Namespace, Network policies and Quotas. They are very cheap and easy and as they are Kubernetes native, every controller already understands namespaces
Some of the core issues of using namespaces per tenant are the following:
- Cluster-scoped objects are shared. For example
CustomResourceDefinition,ClusterRoleorPersistentVolume, and admission webhook configs live outside any namespace. If Tenant A installs a CRD with the same group/version as one Tenant B needs at a different schema, you have a conflict with no boundary to save you. This is the single biggest structural limitation of namespace-based tenancy: There is no way to give two tenants different versions of the same CRD in one cluster. - The API server, etcd, scheduler, and kubelet are shared. A tenant that manages to trigger API server overload affects everyone. There's no per-tenant API server.
- Lack of network segmentation. Without a CNI that allows for Network Policies, there is no network boundary between namespaces and pods can communicate across them.
- Nodes are shared across namespaces. Unless using tains and tolerations to bound teams to nodes, there not only is a noisy neighbor risk, but if an attacker escapes to the underlying node, shared across namesapces, namespace level enforcements can theoretically be bypassed.
Because of that, namespaces are well used for internal teams with the same trust boundary, where gloabl resources dont need to be different by tenant. To have some level of security, it is advised to bind roles to namespaces, use networkpolicies if possible, quotas and also an external policy engine for further enforcements. Moreover creating a new namespace is a cluster operation and unless you want to give teams the permissions to create namespaces and adjust ResourceQuotas, this becomes a bottleneck quickly.
The following graphic highlights the pattern and what is shared across namespaces:

Cluster-per-tenant
On the other hand, one can give every tenant a fully separate Kubernetes cluster, with a separate API server, separate etcd, separate nodes. This eliminates most of the issues I defined for namespace isolation, however comes with drawbacks itself.
- Duplication of components. Control plane and core components like the monitoring-stack or CNI get duplicated. This leads to additional cost and gets more complx to manage at scale.
- No resource sharing across tenants. A quiet tenant's idle capacity can't be borrowed by a busy tenant's cluster. You either overprovision every cluster or accept poor utilization.
- Fleet management becomes its own discipline. Before you start creating clusters for tenants, you should build a system to manage them centrally and keep drift to a minimum.

VCluster: virtual control planes inside a host cluster
- Compute isolation is only as strong as the underlying node model. In the default shared-nodes mode, a "tenant" is still just Linux cgroups/namespaces on a shared kernel, the same compute isolation strength as plain namespaces. To get materially stronger compute isolation you move to private nodes, which reduces the resource-sharing efficiency argument.
- Per-tenant control-plane overhead adds up. Each vCluster is its own API server + datastore process that needs CPU/memory headroom, needs upgrading, and can itself become unhealthy (etcd/SQLite corruption, API server OOMs) independent of the host. Running hundreds of vClusters means monitoring hundreds of small control planes.
- Not all resources/features virtualize cleanly. Certain operators that manage cluster-wide state (service meshes, some CNIs, cert-manager cluster issuers) don't virtualize well and often need to live only at the host level, which tenants then don't have control over.
- The host cluster is still a single point of compromise. If the host cluster's control plane is compromised, or if RBAC on the host cluster is misconfigured, an attacker can potentially reach into every vCluster's synced resources, as they are all stored in the hosts etcd. For instance, when a developer inside
vcluster-teamBcreates a Secret with a database password, vCluster's syncer mirrors it down into the host cluster as a real object in namespacehost-ns-teamB. If someone on the host cluster has a broad ClusterRole likeget/list secretsbound cluster-wide, they can now runkubectl get secret db-password -n host-ns-teamB -o yamland read team B's credentials directly from the host, even though they have zero access tovcluster-teamBitself and team B's vCluster RBAC would have denied them.
VCluster works well for Platforms that need to hand tenants real CRD/operator installation, or more generally isolation at the API-Server level. It also allows for running some shared components like observability inside the host cluster and reduce overhead that way. One also needs to keep in mind that all tenants still live within the same host cluster and there always exist potential escape vectors out of a vCluster.

Capsule
Tenant custom resource that groups namespaces and cascades policy onto them. You install the Capsule controller (a single Deployment, plus a set of CRDs: Tenant, CapsuleConfiguration, GlobalTenantResource, TenantResource, ProxySettings). Cluster admins create a Tenant object per team/customer and specify owners. Tenant Owners then self-service-create namespaces that automatically get attached to their tenant, without requiring intervention from a cluster admin. Everything defined at the Tenant level is automatically inherited by every namespace in that tenant. Via Admission rules one can enforce policies on tenant objects, like allowed image registries, forbidden security contexts or network policy templates.list / watch requests like kubectl get namespaces down to only the objects that belong to the requesting tenant. As Capsule is just one controller and a few CRDs it is very lightweight and doesnt introduce a new abstraction apart from tenants. However, Capsule is still namespace based isolation, just improved for scale. Capsule does not give tenants their own API server, so it inherits every constraint from section 1: no per-tenant CRD versions, no cluster-scoped resource isolation beyond what Capsule Proxy filters at read time, shared blast radius on the real API server/etcd.
KCP
Every model discussed so far treats the Kubernetes API server as inseparable from the rest of the cluster. You get a control plane because you got a cluster, and that cluster comes with a scheduler, kubelets, nodes, and everything required to execute workloads. Kcp breaks this assumption by separating the Kubernetes API from workload execution entirely. Instead of creating a dedicated control plane for every tenant, kcp provides lightweight workspaces. A workspace behaves like an independent Kubernetes cluster from the client's perspective: It has its own API endpoint, RBAC configuration, CRDs, admission webhooks, and API discovery. Workspaces are logical clusters managed by a shared kcp control plane and stored within a shared etcd instance. This allows large numbers of isolated workspaces to be created without provisioning additional control-plane components.
The isolation model in kcp is centred around the Kubernetes API rather than compute. Each workspace owns its own API surface and can define custom resources, admission policies, and access control independently of other workspaces. Platform operators can further curate the APIs visible to tenants through APIExports and APIBindings. Rather than exposing the complete Kubernetes API, they can selectively publish higher-level abstractions while hiding infrastructure-specific resources. This enables application teams to interact with a simplified, domain-specific API while the implementation details remain under the control of the platform. These workspaces can additionally be organized hierarchically. Instead of existing as unrelated entities, they may form trees reflecting organisational structures such as departments, projects, or deployment environments. APIs and administrative responsibilities can be delegated across these hierarchies, allowing platform teams to manage shared services centrally while individual teams retain autonomy within their own branch.
The most fundamental characteristic of kcp is that it deliberately omits the compute layer. There is no scheduler, no kubelet, no nodes, and no workloads executing inside a workspace. Kubernetes objects exist purely as API resources until they are synchronized into one or more execution clusters by external controllers. Consequently, kcp focuses exclusively on API and control-plane management while delegating workload placement and execution to external infrastructure. This infrastructure does not have to be a Kubernetes cluster. The key idea in kcp is that the control plane manages desired state, and controllers reconcile that state into whatever backend they are written for.

Comparison at a glance
| Dimension | Namespaces | Cluster-per-tenant | VCluster | Capsule | Kcp |
| Isolation unit | Namespace | Whole cluster | Virtual control plane (pod) | Tenant (group of namespaces) | Workspace |
| Per-tenant CRDs / API isolation | No | Yes | Yes | No | Yes |
| Per-tenant control plane | No | Yes | Yes (virtual) | No | No (shared process, logical partition) |
| Compute / kernel isolation | Shared | Full | Depends on node mode (shared / private) | Shared | None (no compute layer at all) |
| Provisioning speed | Seconds | Tens of Minutes | Seconds to Minutes | Seconds | Seconds |
| Operational overhead | Low | High | Medium | Low | Low for kcp itself |
| Governance / maturity | Upstream Kubernetes | Upstream Kubernetes | Single-vendor OSS + commercial layer | CNCF Sandbox | CNCF Sandbox |
How to choose
If you are building an internal developer platform or SaaS control plane with a large number of tenants who mainly consume curated, higher-level APIs rather than directly running arbitrary containers, and you are willing to own the operator/syncer layer that turns those APIs into real workloads, kcp gives you the most scalable isolation of the bunch, at the cost of being the least mature.