Kubernetes Cost Governance: Policies That Stop Waste Before It Ships A Cast AI report finds 69% of Kubernetes clusters are CPU-overprovisioned in 2026, up from 40% in 2024, with average CPU utilization at 8%, and the company advocates for admission-time policies like ResourceQuotas and LimitRanges over quarterly cleanup campaigns to structurally prevent waste. Key takeaways - 69% of Kubernetes clusters are CPU-overprovisioned in 2026, up from 40% in 2024. Quarterly cleanup campaigns don’t fix the structural causes of that overprovisioning. - ResourceQuotas cap aggregate namespace consumption across CPU, memory, and object counts. LimitRanges inject per-container defaults and enforce min/max bounds at admission time. - Deploy LimitRange before or alongside ResourceQuota. The LimitRange admission controller runs first, and pods without explicit resource declarations receive HTTP 403 errors if no LimitRange is present to supply defaults. - Admission webhooks Kyverno or OPA Gatekeeper validate and mutate resources at admission time, before they persist to etcd. Both tools support audit mode for safe brownfield migration. - Shift-left tooling Conftest, Kyverno CLI, Argo CD catches policy violations at PR time, when correction costs the least. - Cast AI complements governance policies by rightsizing actual requests and limits to match observed workload behavior, recovering headroom that policies alone cannot reclaim. Why policies beat cleanup Most platform teams have run a cost cleanup campaign. They audit running workloads, find containers with request values that dwarf actual usage, patch the definitions, and watch the bill drop. Then, six months later, the problem is back. The reason is structural. Helm chart authors don’t know your workload. They pick conservative resource defaults that won’t break on deploy, and those defaults ship to production. Teams move on to the next feature and never revisit the resource definitions. Cost is invisible at the team level because most organizations still don’t implement chargeback: according to the FinOps Foundation’s 2025 report, only 14% of teams have it in place. The numbers compound over time. According to the Cast AI 2026 State of Kubernetes Optimization Report, 69% of clusters are CPU-overprovisioned today, up from 40% in 2024. Memory overprovisioning is even worse, with 79% of clusters affected. Average CPU utilization across the industry sits at 8%. At that utilization rate, a $50,000/month cluster wastes roughly $46,000/month. A CNCF FinOps Microsurvey found that 49% of organizations saw cloud spend increase after migrating to Kubernetes, and 70% of those cited overprovisioning as the cause. Cleanup doesn’t change those numbers because it doesn’t change the process that produces them. Policies do. Instead of auditing running workloads every quarter, you define guardrails at the point of admission. A team cannot ship a pod without resource limits because the cluster rejects it. A namespace cannot consume more than its allocated CPU budget because the quota blocks it. The discipline becomes structural, not aspirational. For a broader view of cost control at the Kubernetes layer, the Kubernetes FinOps https://cast.ai/blog/kubernetes-finops/ guide covers budgeting, showback, and chargeback alongside policy enforcement. When costs spike unexpectedly despite policies in place, cost anomaly detection https://cast.ai/blog/kubernetes-cost-anomaly-detection/ gives you the signal before the bill arrives. ResourceQuotas and LimitRanges Kubernetes ships with two built-in admission controllers that handle most of the heavy lifting for namespace-level cost governance. They are complementary, and they need to be deployed in the right order. ResourceQuota: aggregate namespace caps A ResourceQuota defines aggregate limits at the namespace level. You specify how much total CPU and memory the namespace can request and limit, plus object count caps for pods, secrets, ConfigMaps, and other API objects. The object count caps matter more than most teams realize. Runaway Helm releases stack up secrets on every upgrade, and the Kubernetes API server pays the cost of storing and serving them. apiVersion: v1 kind: ResourceQuota metadata: name: team-quota namespace: team-alpha spec: hard: requests.cpu: "4" Total CPU requests allowed in this namespace requests.memory: "8Gi" Total memory requests allowed limits.cpu: "8" Total CPU limits allowed burst headroom above requests limits.memory: "16Gi" Total memory limits allowed pods: "20" Hard cap on pod count prevents runaway scaling secrets: "20" Protects API server from runaway Helm secrets ResourceQuotas apply to new resource creation only – adding a quota to an existing namespace does not terminate running pods that exceed it. This makes quota enforcement safe to roll out incrementally: existing workloads keep running while you measure their consumption and calibrate the quota values before flipping to strict enforcement. LimitRange: per-container defaults and bounds A LimitRange operates at the container level. It injects default requests and limits for containers that don’t declare them, and it enforces minimum and maximum bounds per container. This is the mechanism that prevents a single container from claiming 64 CPUs on a shared cluster. apiVersion: v1 kind: LimitRange metadata: name: default-limits namespace: team-alpha spec: limits: - type: Container default: Injected when a container omits limits entirely cpu: "500m" memory: "256Mi" defaultRequest: Injected when a container omits requests entirely cpu: "100m" memory: "128Mi" max: Hard ceiling per container; admission rejects anything above cpu: "2" memory: "2Gi" min: Floor per container; prevents zero-resource declarations cpu: "50m" memory: "64Mi" The ordering constraint and the HPA trap There is a critical ordering constraint to get right. The LimitRange admission controller runs before the ResourceQuota check. If you deploy a ResourceQuota without a LimitRange and a pod arrives without explicit resource declarations, the API server returns HTTP 403 because it cannot compute whether the pod fits within the quota. Deploy LimitRange first, or apply both objects in the same operation. There is also a less-obvious interaction with the Horizontal Pod Autoscaler. When a namespace quota is exhausted and an HPA tries to scale a deployment, the HPA emits an AbleToScale: False condition and the ReplicaSet controller records FailedCreate events – but the Deployment itself shows no obvious error status. Operators often miss quota exhaustion because it surfaces in events, not in kubectl get deployments output. Add quota-exhaustion alerting to your monitoring setup so the signal is clear and actionable. For a detailed configuration reference covering scope selectors, priority class quotas, and additional edge cases, see the ResourceQuotas and LimitRanges https://cast.ai/blog/kubernetes-resource-quotas-limitranges/ deep dive. This post stays at the conceptual level; that one has the full YAML reference. Before you enforce Before rolling out ResourceQuotas to existing namespaces, run a pre-enforcement audit: kubectl describe resourcequota -n