Kubernetes multi-tenancy cost isolation: quotas, limits, and who pays for the noisy neighbour
Shared clusters are far cheaper than cluster-per-team, but only if one team cannot consume the capacity everyone else paid for. ResourceQuotas, LimitRanges, and priority classes are the controls that make shared infrastructure fair. Here is how to set them.
Quick answer
Shared multi-tenant clusters cut fixed overhead dramatically, 24 single-team clusters cost about $6,800 per month at the floor versus $850 for three shared ones, but they need cost isolation or one team's runaway Deployment becomes everyone's bill. The three controls are ResourceQuota to cap a namespace's total requests, LimitRange to set per-pod defaults and maximums, and PriorityClass to decide who gets evicted under pressure. Set quotas at roughly 120 percent of a team's measured p95 usage, enforce LimitRange defaults so an unspecified pod cannot claim a whole node, and bill each namespace on its requests rather than its usage to make hoarding expensive.
The strongest cost argument in Kubernetes is consolidation: fewer, bigger clusters have less fixed overhead, better bin packing, and fewer control plane fees. The strongest objection is fairness. If everyone shares a pool of nodes, what stops one team from eating it, and how do you split the bill?
What consolidation is worth
| Topology | Clusters | Control plane | Minimum nodes | Monthly floor |
|---|---|---|---|---|
| Cluster per team per environment | 24 | $1,752 | 72 | $6,792 |
| Cluster per environment, namespace per team | 3 | $219 | 9 | $849 |
That is an 87 percent reduction in fixed cost before any workload runs, plus better packing because 8 teams' pods can fill each other's gaps instead of each team padding its own cluster. The prize is large enough to be worth solving the fairness problem properly.
ResourceQuota is the spending cap
A ResourceQuota caps the total CPU, memory, storage, and object counts a namespace can claim. It is the closest thing Kubernetes has to a budget, and it is enforced at admission time, so a team literally cannot exceed it.
| Quota field | What it caps | Cost relevance |
|---|---|---|
| requests.cpu | Total CPU requested | Directly drives node count |
| requests.memory | Total memory requested | Drives node count on memory-bound pods |
| requests.storage | Total PVC capacity | Stops a 20 TB accident |
| count/services.loadbalancers | Number of cloud load balancers | $25 to $50 each per month |
| requests.nvidia.com/gpu | GPUs claimable | $400 to $2,700 each per month |
The load balancer and GPU quotas are the highest-value ones and the most commonly missing. A namespace without a loadbalancers quota can create 30 cloud load balancers with a Helm install, roughly $900 per month. A namespace without a GPU quota can claim four A100s, over $10,000 per month, with one YAML line.
LimitRange sets the per-pod floor and ceiling
ResourceQuota has an important side effect: once a namespace has a CPU or memory quota, every pod in it must specify requests, or admission fails. LimitRange solves that by supplying defaults, and it also caps how large a single pod can be.
A sensible LimitRange sets a defaultRequest around 100m CPU and 128 MB memory so unspecified pods are small rather than large, and a max of perhaps 4 CPU and 16 GB so no single pod can claim most of a node without an explicit exception. Without a max, one pod requesting 30 vCPU forces the autoscaler to provision an entire large node that nothing else can share.
PriorityClass decides who loses
Quotas prevent over-claiming. Priority classes decide what happens when the cluster is genuinely full. Define at least three: a high priority for production user-facing workloads, a default for normal services, and a low or negative priority for batch and CI. Low-priority pods get preempted first, which means batch can safely use spare capacity without threatening production.
This unlocks a real saving. Instead of provisioning separate capacity for batch, let batch run at low priority on the shared pool and absorb whatever is idle. A cluster with 30 percent average headroom can run substantial batch work at effectively zero marginal node cost, as long as those jobs tolerate preemption.
Bill on requests, not usage
Here is the policy choice that makes the whole thing work. If you charge teams for what they use, hoarding quota is free and every team will request 3 times what it needs just in case. If you charge teams for the capacity they reserve, requesting has a price and teams give back what they do not need.
| Billing basis | Team incentive | Typical cluster utilization |
|---|---|---|
| Actual usage | Hoard quota, pad requests | 30 to 40 percent |
| Quota held | Release unused quota | 60 to 75 percent |
| Requests set | Right-size requests | 55 to 70 percent |
Charging on requests is the middle ground most teams land on, because it maps directly to what drives node count and it rewards exactly the behaviour you want. Our guide tocost per namespace covers how to compute the split, and showback by team covers presenting it.
Shared costs still need a home
Some cost does not belong to any namespace: the control plane fee, system DaemonSets, the ingress controller, monitoring, and the idle headroom the cluster keeps for burst. That is typically 15 to 25 percent of the bill. Allocate it proportionally to each namespace's requested capacity rather than leaving it unattributed, otherwise the numbers never reconcile to the invoice and teams stop trusting them.
Quotas, limit ranges, and node pool sizes all live in Terraform or Helm. Price them against theresource catalog so raising a namespace quota by 40 vCPU shows its monthly cost in the pull request, which turns a quota request into a budget conversation.
FAQ
Is a shared Kubernetes cluster cheaper than one cluster per team?
Substantially. Twenty-four single-team clusters cost roughly $6,792 per month at the floor, $1,752 in control plane fees plus a minimum node count per cluster. Three shared clusters with namespace isolation cost about $849, an 87 percent reduction, and they also pack better because different teams' pods fill each other's gaps instead of each team padding its own cluster.
How do I stop one team consuming a shared Kubernetes cluster?
Use ResourceQuota to cap total requested CPU, memory, and storage per namespace, enforced at admission so it cannot be exceeded. Add quotas on count/services.loadbalancers and GPU requests, which are the highest-value and most commonly missing ones: an unbounded namespace can create 30 cloud load balancers for about $900 per month or claim four A100s for over $10,000.
What does LimitRange do for Kubernetes cost?
It supplies default requests for pods that do not specify them, which matters because a namespace with a CPU or memory quota rejects pods without requests, and it caps how large a single pod can be. Without a maximum, one pod requesting 30 vCPU forces the autoscaler to provision an entire large node that nothing else can share.
Should teams be billed on Kubernetes usage or requests?
Charging on requested capacity, or on quota held, works far better than charging on actual usage. If usage is the basis, hoarding quota is free and every team pads its requests, leaving clusters at 30 to 40 percent utilization. Charging on requests or quota makes capacity cost something, and clusters typically settle at 55 to 75 percent utilization instead.
How do I allocate shared Kubernetes costs between teams?
Costs that belong to no namespace, the control plane fee, system DaemonSets, the ingress controller, monitoring, and idle burst headroom, typically total 15 to 25 percent of the bill. Allocate them proportionally to each namespace's requested capacity. Leaving them unattributed means the per-team numbers never reconcile to the invoice, and teams stop trusting the showback entirely.
How does C3X help with multi-tenant Kubernetes cost?
Quotas, limit ranges, and node pool sizes live in Terraform and Helm values, so C3X prices them against a live catalog before merge. Raising a namespace quota by 40 vCPU shows its monthly cost in the pull request, which turns what is usually an unexamined capacity request into an explicit budget conversation between the team and the platform owner.
What to do next
Make shared cluster capacity a priced decision. C3X reads your Terraform and prices your resources against a live catalog. Start with the quickstart.
Share this post
Try C3X on your own Terraform
Free and open source. No API key required. One command to install, one command to estimate.