ResourceQuotas are the namespace-level spending limit. They cap the total amount of resources (CPU, memory, storage, object count) that all Pods in a namespace can collectively consume. This prevents one team from monopolizing the cluster.
1. Compute Resource Quotas
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: team-alpha
spec:
hard:
requests.cpu: "8" # Total CPU requests across all Pods
requests.memory: 16Gi # Total memory requests
limits.cpu: "16" # Total CPU limits
limits.memory: 32Gi # Total memory limits
pods: "20" # Maximum number of Pods
How It Works
# Current state: kubectl describe quota compute-quota -n team-alpha # Name: compute-quota # Resource Used Hard # -------- ---- ---- # limits.cpu 6 16 # limits.memory 12Gi 32Gi # pods 8 20 # requests.cpu 3 8 # requests.memory 8Gi 16Gi # If a developer tries to create a Pod requesting 6 CPU (would total 9): # → REJECTED: exceeded quota (requests.cpu: 9 > hard limit 8)
The LimitRange Requirement
When a ResourceQuota for compute resources exists, every Pod must specify requests and limits. Otherwise the quota can't account for the Pod's usage. If a Pod doesn't specify them, it's rejected.
# Common error:
# "Error: pods "web" is forbidden: failed quota: compute-quota:
# must specify limits.cpu, limits.memory, requests.cpu, requests.memory"
# Solution: Pair ResourceQuota with a LimitRange that provides defaults:
apiVersion: v1
kind: LimitRange
metadata:
name: default-resources
namespace: team-alpha
spec:
limits:
- type: Container
defaultRequest:
cpu: 100m
memory: 128Mi
default:
cpu: 500m
memory: 256Mi
2. Object Count Quotas
Limit how many objects of each type can exist in a namespace:
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
namespace: team-alpha
spec:
hard:
pods: "20"
services: "10"
services.loadbalancers: "2" # Expensive! Limit these
services.nodeports: "5"
secrets: "50"
configmaps: "50"
persistentvolumeclaims: "10"
count/deployments.apps: "10" # Generic format: count/.
count/jobs.batch: "5"
count/cronjobs.batch: "3"
Generic Count Syntax
# Format: count/. count/deployments.apps # Deployments in apps group count/statefulsets.apps # StatefulSets count/ingresses.networking.k8s.io # Ingresses count/roles.rbac.authorization.k8s.io # Roles
3. Storage Quotas
apiVersion: v1
kind: ResourceQuota
metadata:
name: storage-quota
namespace: team-alpha
spec:
hard:
requests.storage: 500Gi # Total PVC storage requests
persistentvolumeclaims: "10" # Max number of PVCs
# Per-StorageClass quotas:
gp3-encrypted.storageclass.storage.k8s.io/requests.storage: 200Gi
gp3-encrypted.storageclass.storage.k8s.io/persistentvolumeclaims: "5"
premium-iops.storageclass.storage.k8s.io/requests.storage: 100Gi
premium-iops.storageclass.storage.k8s.io/persistentvolumeclaims: "2"
4. Quota Scopes — Targeting Specific Pods
Scopes let you apply quotas only to Pods matching certain criteria:
| Scope | Matches Pods That... | Use Case |
|---|---|---|
BestEffort | Have QoS class BestEffort | Limit low-priority workloads |
NotBestEffort | Have QoS class Guaranteed or Burstable | Limit "real" workloads |
Terminating | Have activeDeadlineSeconds set | Limit Jobs/short-lived Pods |
NotTerminating | Don't have activeDeadlineSeconds | Limit long-running services |
PriorityClass | Match a specific PriorityClass | Per-priority budgets |
Priority Class Scoped Quotas
# Different budgets for different priority levels:
apiVersion: v1
kind: ResourceQuota
metadata:
name: high-priority-quota
namespace: team-alpha
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
pods: "5"
scopeSelector:
matchExpressions:
- scopeName: PriorityClass
operator: In
values: ["high"]
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: low-priority-quota
namespace: team-alpha
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
pods: "20"
scopeSelector:
matchExpressions:
- scopeName: PriorityClass
operator: In
values: ["low", "batch"]
5. Multi-Tenancy Pattern
# For a shared cluster with 3 teams: # Total cluster: 48 CPU, 128Gi memory # team-alpha namespace: 16 CPU, 40Gi (33% of cluster) # team-beta namespace: 16 CPU, 40Gi (33% of cluster) # platform namespace: 8 CPU, 32Gi (shared services) # Buffer (unallocated): 8 CPU, 16Gi (headroom for bursting) # Each namespace gets a ResourceQuota + LimitRange: # Quota = budget ceiling # LimitRange = per-Pod guardrails (min/max/defaults)
Summary
| Concept | Key Point |
|---|---|
| ResourceQuota | Namespace-level hard cap on total resources consumed |
| Compute quotas | Cap requests.cpu/memory, limits.cpu/memory, pod count |
| Object count quotas | Cap number of Services, PVCs, Secrets, etc. |
| Storage quotas | Cap total PVC storage — can be per-StorageClass |
| Enforcement | At creation time only — existing Pods are not killed |
| LimitRange pairing | Required when compute quota exists (Pods must have resource specs) |
| Scopes | Apply quotas to subsets (BestEffort, PriorityClass, Terminating) |
| Multi-tenancy | Quota per namespace = budget per team |
📝 Quiz: ResourceQuotas
Q1: A namespace has a quota of requests.cpu: 4. Current total usage is 3.5 CPU. A developer tries to create a Pod with requests.cpu: 600m. What happens?
Q2: You create a ResourceQuota with limits.memory: 8Gi. A developer creates a Pod without any resource spec. What happens?
default values — the LimitRanger injects them automatically.Q3: The quota is reached (pods: 20). An existing Pod crashes (CrashLoopBackOff). Does the controller create a replacement?
Q4: Can a ResourceQuota kill running Pods if the quota is reduced below current usage?
Q5: What's the format for limiting the number of Deployments in a namespace via ResourceQuota?
count/deployments.apps. The generic format is count/<resource>.<api-group>. For core group resources (Pods, Services), use the short names directly (pods, services). For non-core groups, use the full format.Q6: Team Alpha has a quota of 8 CPU. They deploy 4 Pods at 2 CPU request each (total: 8). Now their cluster autoscaler wants to add a 5th Pod. Can they use a PriorityClass-scoped quota to get more?