All articles
Multi-Tenancy Patterns and Which One Is Correct for You
Between a namespace per tenant and a dedicated cluster per tenant lies a whole spectrum of Kubernetes isolation patterns — this post walks through them.
·
Dominik Heilbock

Namespaces
A Namespace is 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 global resources don't 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.
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 complex 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-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
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 doesn't 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.
Comparison at a glance
Dimension | Namespaces | Cluster-per-tenant | VCluster | Capsule |
Isolation unit | Namespace | Whole cluster | Virtual control plane (pod) | Tenant (group of namespaces) |
Per-tenant CRDs / API isolation | No | Yes | Yes | No |
Per-tenant control plane | No | Yes | Yes (virtual) | No |
Compute / kernel isolation | Shared | Full | Depends on node mode (shared / private) | Shared |
Provisioning speed | Seconds | Tens of Minutes | Seconds to Minutes | Seconds |
Operational overhead | Low | High | Medium | Low |
Governance / maturity | Upstream Kubernetes | Upstream Kubernetes | Single-vendor OSS + commercial lay | CNCF Sandbox |
Biggest structural gap | No CRD/cluster-scoped isolation | Cost & fleet sprawl | Syncer can't losslessly translate everything | Still shares one control-plane |
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 separation, separate clusters per tenant is the most proven way, paired with centralized, automated cluster management to tame the operational overhead.
Summary
This Blogpost compared multiple multi-tenancy options and tried to outline how they differentiate from 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 specialize in platform engineering and Kubernetes multi-tenancy. Get in touch if you'd like help finding the right approach for your setup.
Share