Tenancy Best Practices
A field-tested checklist for running many teams and environments on shared Kubernetes clusters safely. The theme running through it is default-deny: start closed, open only what a tenant needs, and make every boundary explicit.
How to Use This List
Read the groups in order - they move from namespace design outward to network, resources, security, and operations.
Treat each bold item as a rule with a rationale, not a suggestion.
Adopt the whole set for new clusters; for existing clusters, apply group A and C first since they deliver the most containment for the least effort.
Encode the rules as templates or policies so new tenants inherit them automatically rather than by memory.
A - Namespace & Identity Design
Make the namespace the tenant unit. Model one team, app, or environment per namespace and bind all policy to it, since a namespace alone isolates nothing.
Template the tenant, not just the namespace. Provision namespace, RBAC, quota, LimitRange, and default-deny NetworkPolicy as one reviewable bundle so no tenant ships half-configured.
Bind RBAC to groups, not people. Reference identity-provider groups in RoleBindings so membership is managed upstream and offboarding is automatic.
Scope permissions with RoleBindings. Grant a shared ClusterRole per-namespace via RoleBinding so each tenant gets identical rights limited to their own scope.
Give each workload its own ServiceAccount. Never run tenant pods under the default ServiceAccount, and disable token automount where the workload does not call the API.
B - Network Isolation
Default-deny between namespaces. Apply an empty-podSelector ingress deny policy to every namespace so cross-tenant traffic is blocked until explicitly allowed.
Allow only intended flows. Layer narrow allow policies on top - same-tenant pods, DNS to kube-system, and named egress - keeping each cross-tenant rule rare and reviewed.
Confirm your CNI enforces policy. NetworkPolicy objects are silently ignored without an enforcing CNI, so verify with a real reachability test rather than assuming.
Deny egress where leakage matters. Add egress default-deny in sensitive namespaces so a leaked connection string cannot reach another environment's database.
C - Resource Fairness
Attach a ResourceQuota to every tenant. Cap aggregate CPU, memory, storage, and object counts so one team cannot consume the cluster or run up unbounded cost.
Pair quota with a LimitRange. Supply default requests and limits so pods stay admissible once a compute quota makes those fields mandatory.
Bound object counts, not just compute. Quota pods, persistentvolumeclaims, and services.loadbalancers to protect etcd and billable cloud infrastructure.
Set requests and limits on every pod. Requests drive scheduling and QoS; limits cap runtime, and together they keep tenants from starving each other.
Reserve high priority for platform components. Use PriorityClass so ingress, DNS, and critical services preempt best-effort tenants under node pressure - and keep the global default low.
D - Security & Runtime Posture
Enforce Pod Security Standards per namespace. Label tenant namespaces restricted for untrusted workloads and baseline at minimum, blocking privileged and host-namespace pods.
Run containers as non-root. Require runAsNonRoot, drop all capabilities, and use read-only root filesystems so a compromised container has little to work with.
Isolate untrusted tenants at the node. Taint dedicated node pools and use tolerations and affinity, or a sandboxed runtime, for workloads that cannot safely share a kernel.
Restrict Secret access tightly. Scope get/list on Secrets per namespace, since broad read access quietly breaks tenant isolation.
Remember the runtime boundary. Docker builds and runs images in dev, but pods on nodes run under containerd via the CRI - tenancy isolation is a Kubernetes-layer concern, not a Docker one.
E - Operations & Governance
Keep one manifest source per app. Use Kustomize overlays or Helm values across environments so dev, stage, and prod cannot drift apart.
Route prod writes through automation. Grant humans view in production and let a CI/CD ServiceAccount or GitOps controller (Argo CD or Flux) perform writes, with break-glass audited.
Enforce rules with a policy engine. Use Kyverno or Gatekeeper to require quotas, labels, and security fields at admission so a forgotten template cannot slip through.
Monitor usage and blast radius. Track quota utilization, CPU throttling, and OOMKills with Prometheus so you tune limits from real data, not guesses.
Plan for the hard cases. When soft isolation is not enough, escalate to virtual clusters or separate clusters rather than stretching namespace boundaries past their design.
When You Are Done
You should have every tenant provisioned from a single template that includes namespace, group-bound RBAC, quota, LimitRange, default-deny network policy, and a Pod Security label.
Cross-tenant network traffic should be denied by default with only explicit allows.
Production writes should flow through automation, and a policy engine should enforce the rules so posture does not decay as the cluster grows.
If any of those are missing, close the gap before onboarding the next tenant.
FAQs
What is the single highest-value control? A default-deny NetworkPolicy per namespace, since it contains lateral movement and cross-tenant traffic with one object per tenant.
Is a namespace enough to isolate a hostile tenant? No. Namespaces are soft isolation on a shared kernel; hostile or regulated tenants need dedicated node pools, sandboxed runtimes, or separate clusters.
Do I really need both ResourceQuota and LimitRange? Yes. Quota bounds the tenant total and makes requests/limits mandatory; LimitRange supplies the defaults that keep pods admissible.
How do I keep these rules from being forgotten? Encode them as tenant templates plus a policy engine like Kyverno or Gatekeeper that rejects non-compliant objects at admission.
Should developers have prod access? Prefer view only, routing writes through GitOps or CI/CD so no human is in the default prod write path.
Related
- How Multi-Tenancy Works in a Cluster
- Tenancy Basics
- Environment Separation
- Resource Quotas & LimitRanges
- Noisy Neighbor Controls
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).