KubernetesXII · Resource Requests and LimitsResource requests and limits
LimitRange defaults and constraints — namespace-level resource policies
What you'll learn
- Configure LimitRange for default requests and limits
- Set per-container min/max constraints
- Reason about the interaction between LimitRange and explicit Pod specs
- Apply LimitRange to enforce production resource policies
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
A LimitRange is a namespace-level policy that sets default resource requests and limits and enforces per-container constraints. It is the safety net for new Pods that don’t declare resources explicitly. This lesson covers the LimitRange fields, the constraints, and the production discipline around namespace resource policies.
LimitRange anatomy
apiVersion: v1
kind: LimitRange
metadata:
name: default
namespace: team-a-prod
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 128Mi
min:
cpu: 50m
memory: 32Mi
max:
cpu: 2
memory: 4Gi
maxLimitRequestRatio:
cpu: "4"
memory: "2"
The fields:
type: Container: applies to individual containers (the default type).default: default limits for containers that don’t declare limits.defaultRequest: default requests for containers that don’t declare requests.min: minimum allowed requests. A container with a request below this is rejected.max: maximum allowed limits. A container with a limit above this is rejected.maxLimitRequestRatio: max ratio of limit to request. A container with limit > request * ratio is rejected.
Other types:
type: Pod: applies to the Pod as a whole (sum of containers).type: PersistentVolumeClaim: applies to PVC storage requests.
How default requests and limits work
When a Pod is created in a namespace with a LimitRange:
- For each container:
- If
requests.cpuis not set, usedefaultRequest.cpufrom the LimitRange. - If
requests.memoryis not set, usedefaultRequest.memory. - If
limits.cpuis not set, usedefault.cpu. - If
limits.memoryis not set, usedefault.memory.
- If
- Validate against min/max: if any container exceeds
maxor is belowmin, reject the Pod.
flowchart TD
Pod[Pod created] --> Check{Has resources?}
Check -- no --> Default[Apply defaults from LimitRange]
Check -- yes --> Validate[Validate against min/max]
Default --> Validate
Validate --> Pass{Passes?}
Pass -- yes --> Create[Pod created]
Pass -- no --> Reject[Pod rejected]
Production discipline: every namespace should have a LimitRange so new Pods land inside sane resource bounds. A Pod without resources is BestEffort and evicted first under node pressure.
Min/max constraints
The min and max fields enforce hard limits:
spec:
limits:
- type: Container
min:
cpu: 50m
memory: 32Mi
max:
cpu: 2
memory: 4Gi
- A container requesting
cpu: 10m(below min) is rejected. - A container with
limits.cpu: 4(above max) is rejected. - A container with no requests is given the default request (which must be ≥ min).
This prevents:
- A Pod requesting 0 CPU (which would be BestEffort).
- A Pod requesting an entire node’s CPU.
- A Pod with no memory limit (which could OOMKill the node).
maxLimitRequestRatio
The maxLimitRequestRatio enforces that the limit is not
too far above the request:
maxLimitRequestRatio:
cpu: "4"
memory: "2"
A container with requests.cpu: 100m, limits.cpu: 1000m
has ratio 10; above 4. The Pod is rejected.
The ratio prevents:
- A Pod with
requests.cpu: 100m, limits.cpu: 100(the Pod reserves almost no CPU but can use 100 cores). This Pod is BestEffort-like in scheduling but unlimited in throttling.
Production discipline: set maxLimitRequestRatio based on
the workload’s burst pattern. A web service might tolerate
4:1 (4x burst over request); a database should be 1:1
(Guaranteed).
Default requests and limits in practice
The default LimitRange for a production namespace:
apiVersion: v1
kind: LimitRange
metadata:
name: default
namespace: team-a-prod
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 128Mi
min:
cpu: 10m
memory: 16Mi
max:
cpu: 4
memory: 8Gi
maxLimitRequestRatio:
cpu: "4"
memory: "2"
This LimitRange:
- Sets defaults so new Pods have resources.
- Allows small resources (10m CPU is reasonable for a control-plane component).
- Caps large resources (4 CPU, 8Gi is reasonable for an application).
- Prevents unlimited bursting.
Pod-level LimitRange
apiVersion: v1
kind: LimitRange
metadata:
name: pod-defaults
namespace: team-a-prod
spec:
limits:
- type: Pod
default:
cpu: 1
memory: 1Gi
defaultRequest:
cpu: 200m
memory: 256Mi
max:
cpu: 8
memory: 16Gi
A type: Pod LimitRange applies to the sum of all
containers in the Pod. The Pod’s total requests and limits
are validated against the Pod-level defaults.
Use Pod-level LimitRange to enforce:
- The Pod as a whole must have at least 200m CPU / 256Mi memory requested.
- The Pod’s total limits cannot exceed 8 CPU / 16Gi.
PVC LimitRange
apiVersion: v1
kind: LimitRange
metadata:
name: pvc-defaults
namespace: team-a-prod
spec:
limits:
- type: PersistentVolumeClaim
default:
storage: 10Gi
defaultRequest:
storage: 10Gi
max:
storage: 1Ti
A PVC created without spec.resources.requests.storage gets
the default. PVCs with requests above max are rejected.
Production discipline
Pair LimitRange with ResourceQuota:
# LimitRange: per-container
apiVersion: v1
kind: LimitRange
metadata:
name: default
namespace: team-a-prod
spec:
limits:
- type: Container
default: {cpu: 500m, memory: 512Mi}
defaultRequest: {cpu: 100m, memory: 128Mi}
max: {cpu: 4, memory: 8Gi}
# ResourceQuota: namespace total
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-prod-quota
namespace: team-a-prod
spec:
hard:
requests.cpu: "32"
requests.memory: 64Gi
limits.cpu: "64"
limits.memory: 128Gi
LimitRange sets per-container defaults and limits; ResourceQuota caps the total in the namespace. Together, they enforce a complete resource policy.
- Every namespace has a LimitRange. New Pods land inside sane resource bounds.
- Set
maxLimitRequestRatioto prevent BestEffort-like bursting. Ratio 4:1 for web services; 1:1 for databases. - Audit LimitRange on namespace creation. Add to the namespace-onboarding runbook.
Diagnosing LimitRange rejections
A Pod that violates LimitRange is rejected with a clear error:
kubectl apply -f deployment.yaml
# Error from server (Invalid): error when creating "deployment.yaml":
# admission webhook "v1.NamespaceBound" denied the request:
# memory: requested memory "64Mi" is below the minimum "128Mi"
The admission controller (the LimitRange plugin) reports the exact violation. Common errors:
requested memory "X" is below the minimum "Y": increase the request or lower the min.requested memory "X" exceeds the maximum "Y": reduce the request or raise the max.limit "X" exceeds the maximum "Y": reduce the limit or raise the max.limit/request ratio "X" exceeds maximum "Y": increase the request or reduce the limit.
Cross-course references
- The Linux course part
XXXVII-Linux-Resourcescovers cgroup resource limits; LimitRange is the cluster-level policy. - The Ansible course part
XLIX-Ansible-Compliancecovers compliance policies; LimitRange is the cluster-level equivalent. - The Observability course part
LXXXVII-Kubernetes-MetricsServercoverskubectl top; use it to verify LimitRange defaults are appropriate.
Quiz
Knowledge check · 4 questions
Q1. When does a LimitRange's `default` field apply to a container?
Q2. `maxLimitRequestRatio` is used to prevent a Pod from having a very high CPU limit with a very low CPU request (which would make it BestEffort-like in scheduling but unlimited in throttling).
Q3. A team deploys a new Pod to a namespace with no LimitRange. The Pod has no `resources` block. What is the QoS class, and what happens under node pressure?
Namespace: `team-a-prod` with no LimitRange. New Pod has no `resources:` block in its container spec. The cluster has 10 nodes, each with 4 CPU and 4Gi memory. After deployment, node 3 runs low on memory (200Mi available).
Q4. What is the difference between LimitRange and ResourceQuota? When is each the right tool?
Passing score: 75%. Answers are checked in this browser.