Resource Quotas & LimitRanges
Summary
- Definition: A
ResourceQuotacaps the aggregate resources and object counts a namespace may consume; aLimitRangesets per-container defaults, minimums, and maximums. - Insight: They solve different problems. Quota bounds the tenant total; LimitRange shapes each pod. You almost always want both.
- Key Concepts:
requestsvslimits,scopeSelectorfor priority-aware quotas, default injection, and the admission-time enforcement point. - When to Use: Any shared cluster where one team must not be able to consume everyone else's capacity.
- Limitations: Both act only at admission on new objects. Neither reclaims resources from pods already running, and quota does not throttle a single pod - the kernel and limits do that.
Recipe
Attach a ResourceQuota to each tenant namespace to cap total CPU, memory, storage, and object counts.
Attach a LimitRange to the same namespace so pods that omit requests/limits still get sensible values.
Confirm every pod now carries requests and limits, because once a compute quota exists the two become mandatory.
Watch usage and tune the numbers against real consumption over time.
Working Example
Cap the namespace total and bound object counts.
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-compute
namespace: team-a
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
persistentvolumeclaims: "10"
requests.storage: 100Gi
pods: "60"
services.loadbalancers: "2"Supply per-container defaults and guardrails.
apiVersion: v1
kind: LimitRange
metadata:
name: team-a-limits
namespace: team-a
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "2"
memory: 4Gi
min:
cpu: 50m
memory: 64MiApply and inspect current consumption.
kubectl apply -f quota.yaml -f limitrange.yaml -n team-a
kubectl describe resourcequota team-a-compute -n team-a
kubectl describe limitrange team-a-limits -n team-aThe describe output shows Used versus Hard, which is your live budget check.
Deep Dive
Requests vs limits
requests are what the scheduler reserves and what quota counts for requests.cpu/requests.memory.
limits are the ceiling the kernel enforces at runtime and what limits.cpu/limits.memory count.
A namespace can hit its request quota (nothing more will schedule) long before pods hit their runtime limits.
Why quota forces requests and limits
Once a quota constrains a compute resource, the API server rejects any pod that does not declare that resource.
That is the reason LimitRange matters: its default and defaultRequest inject the missing values at admission so teams are not forced to annotate every container.
Without a LimitRange, a quota turns into a wall of must specify limits.cpu rejections.
Object-count quotas
Quota is not only about CPU and memory.
Capping pods, persistentvolumeclaims, services.loadbalancers, and count/<resource> protects shared and billable infrastructure - each LoadBalancer Service may provision a real cloud load balancer.
Bounding object counts also limits etcd growth and API churn from a runaway controller.
Priority-scoped quota
scopeSelector lets you quota by PriorityClass, so a tenant's high-priority budget is separate from its best-effort budget.
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-high-priority
namespace: team-a
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
scopeSelector:
matchExpressions:
- operator: In
scopeName: PriorityClass
values: ["high"]This prevents a tenant from claiming all high-priority capacity while still allowing generous best-effort use.
LimitRange min and max
max blocks a single container from requesting more than a per-container ceiling, so one pod cannot swallow the whole namespace budget.
min prevents unrealistically tiny requests that would let too many pods pack onto a node and cause contention.
Together they keep individual pods within a sane band.
Gotchas
Quota without LimitRange breaks deploys. Teams suddenly cannot create pods because every container needs explicit requests/limits. Always pair them.
Limits far above requests overcommit nodes. A 100m request with a 4-core limit lets many pods schedule then fight for CPU. Keep the ratio disciplined.
Editing quota does not touch running pods. Lowering a quota below current usage only blocks new pods; existing ones keep running until they restart. Plan reductions accordingly.
LimitRange only affects new pods. Applying it does not retrofit running workloads. Roll deployments to pick up new defaults.
Forgetting storage and object counts. A tenant with no PVC or LoadBalancer cap can still run up cost and etcd pressure. Quota the countable resources too.
Memory limit equals OOMKill. A container that exceeds its memory limit is killed by the kernel, not throttled. Size memory limits from real usage, not guesses.
Alternatives
LimitRange only, no quota. Shapes individual pods but leaves the namespace total unbounded. Fine for a trusted single-team cluster, unsafe for real multi-tenancy.
Quota only, no LimitRange. Bounds the total but forces every team to hand-set requests/limits. Workable if a policy engine enforces those values instead.
Policy engine (Kyverno / Gatekeeper). Mutating or validating policies can inject and require requests/limits with more logic than LimitRange, and enforce ratios. Use when you need rules LimitRange cannot express.
Cluster autoscaling (Karpenter). Adds nodes when demand grows rather than capping tenants. Complementary, not a substitute - you still want quota so one tenant cannot scale the bill without bound.
FAQs
Do I need both quota and LimitRange? Almost always. Quota bounds the tenant total and makes requests/limits mandatory; LimitRange supplies the defaults that keep pods admissible.
Why can't my team create pods after I added a quota? A compute quota requires every container to declare that resource. Add a LimitRange with defaults or set requests/limits explicitly.
Does lowering a quota evict pods? No. It only blocks new pods and updates that would exceed the new limit. Running pods continue until they restart.
Can I quota GPUs or custom resources? Yes, via requests.<resource> and count/<resource>.<group>, so extended resources and CRDs can be bounded too.
How do I quota by priority? Use a scopeSelector matching PriorityClass values, giving each tier its own budget within the namespace.
Related
- How Multi-Tenancy Works in a Cluster
- Tenancy Basics
- Noisy Neighbor Controls
- Environment Separation
- Tenancy Best Practices
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).