The article on requests and limits explains how a correctly sized pod behaves. Nothing in that mechanic stops a team from deploying a thousand of them, each perfectly sized individually, until the entire cluster’s capacity is exhausted at every other team’s expense. ResourceQuota and LimitRange answer a different question: not “is this pod correctly sized” but “how much is an entire namespace allowed to consume.”
ResourceQuota: an aggregate cap per namespace
A ResourceQuota caps the sum of resources requested or consumed by every object in a namespace, not a single object.
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-payments-quota
namespace: payments
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
pods: "50"
Once that quota is in place, every new pod created in the payments namespace has to declare explicit requests: without that declaration, the object’s creation is rejected outright by the API server, not just flagged. That’s a side effect often discovered by surprise: setting a ResourceQuota on a namespace where pods were already running without explicit requests blocks any new deployment until every pod gets fixed.
LimitRange: defaults and bounds
Where ResourceQuota caps an aggregate, LimitRange acts pod by pod, container by container, at creation time: it injects default values into what doesn’t declare any, and rejects whatever exceeds an individual ceiling.
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: payments
spec:
limits:
- default:
cpu: "500m"
memory: 512Mi
defaultRequest:
cpu: "250m"
memory: 256Mi
max:
cpu: "2"
memory: 2Gi
type: Container
This LimitRange solves exactly the trap mentioned above: a pod created without explicit requests automatically gets 250m/256Mi, satisfying the ResourceQuota requirement without the developer having to think about it. The two mechanisms combine naturally: LimitRange fills individual gaps, ResourceQuota caps the resulting aggregate.
The trap of a ceiling never revisited
A ResourceQuota set once at namespace launch and never reconsidered becomes either an artificial ceiling blocking legitimate team growth, or a limit so generous it protects nothing anymore. The quota needs the same discipline as any budget: revisited periodically against actual observed usage (kubectl describe resourcequota shows current consumption against the cap), not set once and forgotten.
The link to multi-tenancy
These two mechanisms are the technical counterpart to the isolation described in the article on GitOps at scale: an AppProject restricts which namespaces a team can target, ResourceQuota and LimitRange restrict what it can consume there once deployment is authorized. Scope isolation without a consumption ceiling still lets a legitimately authorized team exhaust the whole cluster’s shared capacity.
Takeaway
ResourceQuota caps a namespace’s aggregate consumption, LimitRange injects default values and bounds each container individually, and the two combine to keep a missed requests declaration from becoming either a creation-time rejection or unchecked consumption. Setting this per-namespace governance, with periodic review, is part of the multi-team foundation of a Kubernetes migration that grows beyond a single application.