Configuring ResourceQuota and LimitRange at the Namespace Level
Overview
In Kubernetes, a namespace is not only a way to sort and group your resources — it is also a powerful place to limit the computing resources that your Pods can consume. In this article you will learn how to protect a shared cluster from a single misbehaving application that tries to eat all the CPU and memory.
Think of your cluster like an apartment building. Each namespace is one apartment, and each Pod is a tenant living inside it. Without house rules, one noisy tenant could run every appliance at once and leave nothing for the neighbors. Kubernetes gives you two "house rules" objects to prevent this: ResourceQuota and LimitRange.
By the end of this article you will understand four building blocks and how they fit together:
- Resource requests — the minimum resources a container needs to start.
- Resource limits — the maximum resources a container is allowed to use.
- ResourceQuota — a total budget for an entire namespace.
- LimitRange — default and boundary values applied to every container.
Core Concepts
Two Kinds of Computing Resources
Kubernetes measures two types of computing resources for your containers:
- CPU — expressed in millicores (
m). A value of1000mequals one full CPU core. So250mis one quarter of a core, and500mis half a core. To request a whole core, it is more common to write1instead of1000m. - Memory — expressed in bytes, usually written with suffixes like
Mi(mebibyte) andGi(gibibyte). OneMiequals 1,048,576 bytes, so512Miis roughly half a gibibyte.
You add more CPU and memory to a cluster simply by adding more worker (compute) nodes — either by buying hardware on-premises, or by calling a cloud API to create more virtual machines.
How Pods Consume Resources
When you create a Pod, a control plane component called the kube-scheduler picks a suitable worker node, and the kubelet on that node starts the containers. This process is called Pod scheduling.
By default, a scheduled Pod can access all the resources on its node. Nothing stops it from grabbing more and more CPU and memory. If ten Pods share one node and a single greedy Pod consumes everything, all ten Pods suffer. This leads to two needs:
- Each Pod should be able to request the resources it needs to work.
- The cluster should be able to restrict a Pod so it does not starve its neighbors.
Resource Requests: the Guaranteed Minimum
A request is the minimum amount of CPU and memory a container needs to run properly. The scheduler reads the request to find a node with enough free room. If the container is placed, that amount of resources is guaranteed to it.
Be careful: if you request more than any single node can offer, the Pod will never be scheduled and will stay in the Pending state forever. Remember that a Pod cannot span multiple nodes — if you request eight cores but every node only has four, no node can satisfy it.
Resource Limits: the Hard Ceiling
A limit is the maximum a container is allowed to consume. Always set a limit whenever you set a request. Kubernetes behaves differently depending on which limit is hit:
- CPU limit reached → the container is throttled (slowed down). You will notice performance degradation, but the container keeps running.
- Memory limit reached → the container may be terminated (OOMKilled). Memory cannot be throttled, so Kubernetes kills the container to protect the node.
ResourceQuota vs. LimitRange
Requests and limits are great — as long as nobody forgets to set them. It is very easy for you or a teammate to deploy a Pod with no request and no limit, which can then eat the whole node. Kubernetes solves this with two namespace-scoped objects:
| Object | Scope | What it controls |
|---|---|---|
ResourceQuota | The whole namespace (all Pods combined) | Total CPU/memory budget and object counts (Pods, Services, ConfigMaps, PVCs, etc.) |
LimitRange | Each individual container/Pod | Default requests/limits, plus min and max boundaries |
These two work best together. When a ResourceQuota exists, every container is required to declare requests and limits. A LimitRange fills in sensible defaults automatically, so nobody's Pod gets rejected just for forgetting them.
Hands-On: Kubernetes Commands
Enable the metrics server (on minikube) so you can inspect real CPU and memory usage:
Check node-level resource usage across the cluster:
Check per-Pod usage inside a namespace:
List the ResourceQuota objects in a namespace and see how much of the budget is used:
Describe a ResourceQuota to view its hard limits and current consumption:
List and describe the LimitRange objects in a namespace:
Inspect why a Pod is stuck in Pending — the events at the bottom explain the cause:
Step-by-Step Example
We will build a small multi-tenant setup for a fictional billing team. We use a .NET 10 ASP.NET Core container as the sample workload. Follow the steps in order.
Step 1: Build the .NET Application Image
Before we can run a container, we need an image. Here is a minimal Dockerfile that builds an ASP.NET Core app on .NET 10. In this article we run the ready-made mcr.microsoft.com/dotnet/aspnet:10.0 runtime image directly, but this shows how a real application image is produced.
Step 2: Create the Namespace
Every quota and limit lives inside a namespace, so we create one first. This isolates the billing team's workloads from the rest of the cluster.
Apply it:
Step 3: Run a Pod With Sensible Requests and Limits
This Pod asks for 256Mi memory and 250m CPU as a guaranteed minimum, and is capped at 512Mi memory and 500m CPU. These are realistic values that a small node can easily satisfy.
Apply it and confirm it reaches the Running state:
Step 4: See What Happens With an Impossible Request
Now let's deliberately request an unrealistic 200Gi of memory. No normal node can offer this, so the scheduler cannot place the Pod, and it stays Pending forever.
Apply it, then inspect why it is stuck:
In the events you will see a message like 0/1 nodes are available: 1 Insufficient memory. This proves that a request larger than any node can never be scheduled. Delete it before continuing:
Step 5: Add a ResourceQuota for the Namespace
A ResourceQuota sets a total budget for everything in the namespace combined. This one says: all Pods together may request at most 1 CPU core and 1Gi of memory, may be limited to at most 2 cores and 2Gi, and the namespace may hold at most 10 Pods, 10 ConfigMaps, and 5 Services.
Apply it and check how much of the budget is already used:
Once this quota exists, if you try to create a Pod that pushes the namespace past the budget, Kubernetes rejects it immediately with a Forbidden: exceeded quota error — the Pod is never created.
Step 6: Add a LimitRange for Default and Boundary Values
A LimitRange protects you from forgetting requests and limits, and blocks containers that are too small or too large. The keys mean:
default— the limit applied automatically when a container sets none.defaultRequest— the request applied automatically when a container sets none.max— the highest limit a container is allowed to declare.min— the lowest request a container is allowed to declare.
Apply it and view the resulting table:
Now any container in tenant-billing that omits its resources automatically inherits 250m/256Mi requests and 500m/512Mi limits, and can never exceed the min/max boundaries.
Step 7: Clean Up
Remove the objects you no longer need (always include -n to target the right namespace):
Summary
Namespaces do far more than group resources — they are the natural boundary for controlling how much CPU and memory your applications can consume. In this article you learned:
- Requests guarantee a minimum and drive scheduling; too-high requests leave a Pod stuck in
Pending. - Limits cap consumption; hitting a CPU limit throttles the container, while hitting a memory limit can terminate it.
- ResourceQuota enforces a total budget for a whole namespace, covering both compute and object counts.
- LimitRange supplies defaults and
min/maxboundaries so no container is left unconstrained.
It is a strong best practice to define a ResourceQuota and a LimitRange for every namespace you create. This dual-layered approach keeps resource-hungry workloads contained both at the namespace level and at the individual container level, giving you a predictable and stable cluster.