Running one cluster per team is simple and ruinously expensive. Sharing a cluster is cheap and terrifying, unless you isolate tenants deliberately. Multi-tenancy done right gives teams a self-service namespace that cannot hurt their neighbors.
Soft vs hard multi-tenancy
Soft: trusted internal teams, isolation by convention plus policy
Hard: untrusted or regulated workloads, isolation by strong boundaries
Most platforms want soft tenancy done rigorously, with an upgrade path
Isolate every namespace by default
A tenant namespace should ship with quotas, limits, a default-deny network policy, and scoped RBAC baked in, never a bare namespace.
apiVersion: v1
kind: ResourceQuota
metadata: { name: tenant-quota, namespace: team-a }
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
pods: "100"
---
apiVersion: v1
kind: LimitRange
metadata: { name: defaults, namespace: team-a }
spec:
limits:
- type: Container
default: { cpu: 500m, memory: 512Mi }
defaultRequest: { cpu: 100m, memory: 128Mi }Add a default-deny NetworkPolicy and a RoleBinding scoped to the tenant, and one noisy neighbor can no longer starve the cluster or reach into another namespace.
When you need harder walls
For stronger isolation, reach for virtual clusters (vcluster) or hierarchical namespaces so tenants get their own API surface without their own control plane:
vcluster create team-a --namespace team-a-host
vcluster connect team-a -- kubectl get namespacesMulti-tenancy is not a feature you enable. It is a set of boundaries you refuse to leave off.
Templatize the secure namespace, automate its creation, and give teams real self-service inside walls they cannot accidentally cross. Cheap and safe is possible, it just has to be the default.



