Gemini Generated Image 152pr0152pr0152p

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, ClusterRole or PersistentVolume, 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.
     
Having dedicated clusters per tenant, makes sense if tenants require strict isolation. In these cases you need to have a solid fleet management architecture in place first, e.g via a Hub and Spoke model with ClusterAPI, which takes significant time to build up. Also keep in mind that if network traffic between clusters is possible, tenants are never truly isolated.

 
 

VCluster: virtual control planes inside a host cluster

VCluster sits between namespaces and full clusters. The core idea: give each tenant a Kubernetes control plane with its own API server and run that control plane as a workload inside a namespace of a shared host cluster. A vCluster's control plane runs in a StatefulSet pod. From the host cluster's point of view, it's just a pod in a namespace. The so-called syncer watches objects created inside the virtual cluster and translates them into real objects in the host cluster's namespace so they can actually be scheduled and run. Critically, higher-level resources never leave the virtual cluster.A `Deployment`, `StatefulSet`, or a CRD you install inside the vCluster exists only in the virtual API server/datastore. The host cluster's API server never sees it. This is what gives tenants real CRD and admission-control autonomy without needing cluster-admin on the host. vCluster also has an answer to compute level isolation, as it comes with several node models. Shared nodes: Tenant workloads run as regular pods on the host cluster's shared node pool. With private nodes clusters can mapped to nodes in a 1:1 relationship, there always is just one tenant on one node. Moreover, each virtual cluster gets its own CoreDNS by default, so in-cluster service discovery works normally from the tenant's point of view.
 
However vCluster also comes with some disadvantages:
 
  • 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-teamB creates a Secret with a database password, vCluster's syncer mirrors it down into the host cluster as a real object in namespace host-ns-teamB. If someone on the host cluster has a broad ClusterRole like get/list secrets bound cluster-wide, they can now run kubectl get secret db-password -n host-ns-teamB -o yaml and read team B's credentials directly from the host, even though they have zero access to vcluster-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
 

Capsule takes a different direction than vCluster. It does not give tenants their own control plane within a host cluster, but makes namespace boundaries better suited for multi tennacy, by adding a 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.
To prevent namespace leakage, Capsule implemets the Capsule Proxy. A reverse proxy tenants authenticate through instead of hitting the API server directly. It filters cluster-scoped 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.
 
It works well for platform teams that are happy with namespace-level isolation strength but need self-service namespace provisioning, tenant-scoped policy inheritance, and a "my namespaces only" view.
 
 

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

 

DimensionNamespacesCluster-per-tenantVClusterCapsuleKcp
Isolation unitNamespaceWhole clusterVirtual control plane (pod)Tenant (group of namespaces)Workspace 
Per-tenant CRDs / API isolationNoYesYesNoYes
Per-tenant control planeNoYesYes (virtual)NoNo (shared process, logical partition)
Compute / kernel isolationSharedFullDepends on node mode (shared / private)SharedNone (no compute layer at all)
Provisioning speedSecondsTens of MinutesSeconds to MinutesSecondsSeconds
Operational overheadLow HighMediumLowLow for kcp itself
Governance / maturityUpstream KubernetesUpstream KubernetesSingle-vendor OSS + commercial layerCNCF SandboxCNCF Sandbox
 

How to choose

Based on experience, these are a few decision heuristics that hold up in practice:
 
If there is a small set of "tenants" that are internal teams who trust each other and only need environment/app separation: Namespaces plus RBAC, ResourceQuota, NetworkPolicy, and PSA could be enough.
 
If tenants need to install their own operators/CRDs but you don't want to give them cluster-admin, and you're okay with shared-kernel compute isolation: vCluster is the most direct answer, especially for ephemeral environments (PR previews, CI, sandboxes) where fast teardown matters more than hardened isolation.
 
If you like namespace-level isolation but are tired of manually wiring RBAC/quotas/policy per namespace and want tenants to self-serve namespace creation within guardrails: Capsule is the lowest-overhead way to get there, and it also prevents namespace leakage.
 
If your tenants are external customers, regulated workloads, or need strong seperation, seperate clusters per tenant is the most proven way paired with centralized, automated cluster management to tame the operational overhead.

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.

 

Summary

This Blogpost compared multiple multi tenancy options and tried to outline how they differentiate form each other and subsequently where they make sense. Navigating these trade-offs in your own environment isn't always straightforward, the right choice depends on your team structure, security requirements, and operational maturity. If you're evaluating multi-tenancy strategies or building out a Kubernetes platform for your organization, we specializes in platform engineering and Kubernetes multi-tenancy. Get in touch if you'd like help finding the right approach for your setup.