Quotas & Limits
In a shared cluster, trust is good, but enforcement is better.
Without controls, a single developer with a typo (replicas: 100 instead of 10) can accidentally consume every CPU core in the cluster, causing production outages for everyone else.
To prevent this "Tragedy of the Commons," Kubernetes provides two layers of defense: ResourceQuotas (The Budget) and LimitRanges (The Rules).
The Analogy: Corporate Credit Cards
Think of a Namespace like a Department in a company (e.g., "Engineering").
- ResourceQuota (The Department Budget):
"The Engineering department has a total budget of $10,000/month."
- They can spend it on 1 big server or 100 small ones.
- Once they hit $10,000, their card is declined.
- LimitRange (The Expense Policy):
"No single lunch expense can be over $50."
"If you forget to write down the cost, we assume it was $20."
- This controls individual transactions (Pods).
1. ResourceQuota (The Ceiling)
A ResourceQuota limits the aggregate total of resources used by all objects in a specific namespace.
If a new Pod tries to start, Kubernetes checks: $$\text{Current Usage} + \text{New Pod Request} \le \text{Quota Limit}$$
If the answer is No, the API Server rejects the request with a 403 Forbidden.
Example: The "Hard" Stop
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-budget
namespace: team-a
spec:
hard:
# Compute Resources
requests.cpu: "4" # Max 4 vCPUs reserved
requests.memory: 10Gi # Max 10Gi RAM reserved
limits.cpu: "8" # Max 8 vCPUs limit
limits.memory: 20Gi # Max 20Gi RAM limit
# Object Counts (Prevent "Pod Spam")
pods: "10" # Max 10 Pods total
services.loadbalancers: "2" # Max 2 expensive Cloud LBs
Checking Usage
To see how much budget you have left:
Output:
2. LimitRange (The Defaults & Guardrails)
A LimitRange operates at the Pod/Container level. It does two critical things:
- Enforcement: "Your container cannot be smaller than 100m CPU or larger than 2 CPUs."
- Defaulting (Mutation): "You forgot to set resources? I will set them to 500m/512Mi automatically."
This is the most useful feature for Admins. It ensures that even lazy developers create safe Pods.
Example: Enforcing Standards
apiVersion: v1
kind: LimitRange
metadata:
name: container-limits
namespace: team-a
spec:
limits:
- default: # <--- If user sets nothing, give them this LIMIT
memory: 512Mi
cpu: 500m
defaultRequest: # <--- If user sets nothing, give them this REQUEST
memory: 256Mi
cpu: 250m
max: # <--- No container can exceed this
memory: 1Gi
cpu: 1
min: # <--- No container can be smaller than this
memory: 64Mi
cpu: 100m
type: Container
The Admission Workflow
It is important to understand the order of operations. LimitRanges run first (because they might add default values), and ResourceQuotas run last (to check the final total).
graph TD
User[User applies Pod YAML] -->|1. Request| API[API Server]
API -->|2. Mutating| LR[LimitRange Controller]
LR -- "No resources set? Add defaults." --> Pod[Updated Pod Spec]
API -->|3. Validating| RQ[ResourceQuota Controller]
RQ -- "Is (Current + New) > Quota?" --> Check{Check}
Check -- Yes --> Deny[Error 403: Forbidden]
Check -- No --> Allow[Persist to etcd]
Troubleshooting Common Errors
When these policies block you, the error messages are specific.
Error 1: Quota Exceeded
Error from server (Forbidden): pods "my-pod" is forbidden: exceeded quota: compute-resources, requested: requests.cpu=1, used: requests.cpu=3, limited: requests.cpu=4
Translation: You asked for 1 CPU, but the namespace only has 1 CPU left in the budget. Fix: Delete old pods or ask admin to increase Quota.
Error 2: LimitRange Violation
Error from server (Forbidden): pods "my-pod" is forbidden: minimum cpu usage per container is 100m, but request is 50m.
Translation: Your Pod spec is too small. The admin requires at least 100m.
Fix: Increase your resources.requests.cpu to match the minimum.
Interaction: The "Hidden" Requirement
The Catch-22
If you create a ResourceQuota for CPU/Memory, EVERY Pod in that namespace MUST have requests/limits defined.
If you try to create a "naked" Pod (no resources) in a Quota-enabled namespace, the API will reject it immediately because it cannot calculate the cost.
Solution: Always pair a ResourceQuota with a LimitRange that provides defaults. This ensures "naked" Pods get default values automatically and pass the Quota check.
Summary
| Feature | Scope | Primary Purpose |
|---|---|---|
| ResourceQuota | Namespace (Aggregate) | Budgeting. "Stop the team from using too much." |
| LimitRange | Pod/Container (Individual) | Policy. "Ensure every pod has reasonable defaults and sizes." |
Best Practice:
- Create a ResourceQuota on every namespace to prevent accidents.
- Create a LimitRange on every namespace to inject default requests/limits for developers who forget them.