Resource Requests and Limits
Kubernetes clusters are shared environments. Resource requests and limits help the scheduler place workloads correctly and help prevent one workload from consuming more CPU or memory than it should.
What this lesson covers
So far, we have created workloads without telling Kubernetes how much CPU or memory they need. That works in a small lab, but it is not a good habit for real clusters. Kubernetes needs resource information to schedule workloads responsibly.
By the end of this lesson, you should understand:
- What CPU and memory requests are
- What CPU and memory limits are
- How requests affect scheduling
- How limits affect runtime behavior
- Why memory limits can cause container restarts
- Why CPU limits can cause throttling
- How to inspect resource settings on Pods
- How to create workloads with requests and limits
- How to create an intentionally unschedulable Pod
- How Namespaces can use default resource controls later
The short version
A resource request is what a container asks Kubernetes to reserve for scheduling.
A resource limit is the maximum amount a container is allowed to use.
request: "I need at least this much CPU or memory." limit: "Do not let me use more than this much CPU or memory."
Requests help Kubernetes decide where a Pod can run. Limits help define how much a container can consume once it is running.
CPU and memory units
Kubernetes commonly tracks two basic resources for containers:
- CPU
- Memory
CPU can be written as whole cores or millicores.
1 CPU = one CPU core 500m = half a CPU core 250m = one quarter of a CPU core 100m = one tenth of a CPU core
Memory is usually written with units like Mi or Gi.
128Mi = 128 mebibytes 512Mi = 512 mebibytes 1Gi = 1 gibibyte
Requests vs limits
Requests and limits are related, but they are not the same.
Requests
Requests tell Kubernetes how much resource a container needs for scheduling.
If a Pod requests 256Mi of memory and 250m of CPU, Kubernetes tries to place it on a node that has enough available allocatable capacity for those requests.
Limits
Limits define how much resource the container is allowed to use.
If a container exceeds its memory limit, it can be killed and restarted. If it hits its CPU limit, it may be throttled.
Why requests matter for scheduling
Kubernetes does not schedule based on actual future usage. It schedules based on declared requests.
If a container does not declare requests, the scheduler has less useful information.
A cluster might look fine at first, but over time, workloads without requests can lead to noisy neighbor problems, resource pressure, evictions, and unpredictable performance.
Why limits matter for protection
Limits can prevent a workload from consuming too much of a node.
This is useful when:
- An application has a memory leak
- A process unexpectedly spikes
- A batch job consumes too much CPU
- A shared cluster needs guardrails
- You want to limit blast radius from badly behaved workloads
Limits are not always simple, though. Overly tight limits can cause unnecessary failures or poor performance.
Before you start the lab
This lab assumes:
- Docker Desktop is running
- Your kind cluster exists
- kubectl is installed
- Your current context is kind-stealth-k8s-lab
Check your context:
kubectl config current-context
You should see:
kind-stealth-k8s-lab
Check your node:
kubectl get nodes
If your cluster no longer exists, recreate it:
kind create cluster --name stealth-k8s-lab
Step 1: Create a Namespace for this lab
Create a separate Namespace so this lab is easy to clean up:
kubectl create namespace resource-lab
Confirm it exists:
kubectl get namespaces
Step 2: Create a Pod with resource requests and limits
Create a file named:
resource-pod-lab.yaml
Paste this into the file:
apiVersion: v1
kind: Pod
metadata:
name: resource-pod-lab
namespace: resource-lab
spec:
containers:
- name: app
image: nginx:latest
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
Apply it:
kubectl apply -f resource-pod-lab.yaml
Check the Pod:
kubectl get pod resource-pod-lab -n resource-lab
You should see the Pod running.
Step 3: Inspect the Pod resources
Describe the Pod:
kubectl describe pod resource-pod-lab -n resource-lab
Look for the container resource section. You should see:
Requests: cpu: 100m memory: 128Mi Limits: cpu: 500m memory: 256Mi
This confirms Kubernetes accepted the resource requests and limits.
Step 4: View the Pod YAML
Run:
kubectl get pod resource-pod-lab -n resource-lab -o yaml
Look for:
- spec.containers.resources.requests
- spec.containers.resources.limits
- status
The resource settings are part of the container spec.
Step 5: Create a Deployment with requests and limits
In real workloads, resource settings are usually defined inside a Deployment's Pod template.
Create a file named:
resource-deployment-lab.yaml
Paste this into the file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: resource-deployment-lab
namespace: resource-lab
spec:
replicas: 2
selector:
matchLabels:
app: resource-deployment-lab
template:
metadata:
labels:
app: resource-deployment-lab
spec:
containers:
- name: app
image: nginx:latest
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
Apply it:
kubectl apply -f resource-deployment-lab.yaml
Check the Deployment and Pods:
kubectl get deployments -n resource-lab kubectl get pods -n resource-lab
You should see two Pods created by the Deployment.
Step 6: Describe one of the Deployment Pods
Get the Pod names:
kubectl get pods -n resource-lab
Describe one of the Pods:
kubectl describe pod <pod-name> -n resource-lab
Confirm the same requests and limits are present.
Step 7: Create an unschedulable Pod on purpose
Now create a Pod that asks for far more CPU than your local kind node is likely to have available.
Create a file named:
too-large-pod-lab.yaml
Paste this into the file:
apiVersion: v1
kind: Pod
metadata:
name: too-large-pod-lab
namespace: resource-lab
spec:
containers:
- name: app
image: nginx:latest
resources:
requests:
cpu: "100"
memory: "128Mi"
limits:
cpu: "100"
memory: "256Mi"
Apply it:
kubectl apply -f too-large-pod-lab.yaml
Check the Pod:
kubectl get pod too-large-pod-lab -n resource-lab
It should remain in Pending.
Step 8: Troubleshoot the Pending Pod
Describe the Pod:
kubectl describe pod too-large-pod-lab -n resource-lab
Look at the events near the bottom. You should see a scheduling problem related to insufficient CPU.
This demonstrates that requests affect scheduling. Kubernetes will not schedule a Pod onto a node that cannot satisfy the requested resources.
Step 9: Delete the unschedulable Pod
Delete the oversized Pod:
kubectl delete -f too-large-pod-lab.yaml
Confirm it is gone:
kubectl get pods -n resource-lab
Step 10: Create a memory limit failure on purpose
Now create a Pod that has a very low memory limit and intentionally tries to allocate more memory than allowed.
Create a file named:
memory-limit-pod-lab.yaml
Paste this into the file:
apiVersion: v1
kind: Pod
metadata:
name: memory-limit-pod-lab
namespace: resource-lab
spec:
restartPolicy: Never
containers:
- name: memory-demo
image: python:3.12-slim
command: ["python", "-c"]
args:
- |
data = []
while True:
data.append("x" * 1024 * 1024)
resources:
requests:
memory: "32Mi"
cpu: "50m"
limits:
memory: "64Mi"
cpu: "250m"
Apply it:
kubectl apply -f memory-limit-pod-lab.yaml
Watch the Pod:
kubectl get pod memory-limit-pod-lab -n resource-lab -w
Press Ctrl+C after you see the status change.
The container may show a failure state because it exceeded the memory limit.
Step 11: Inspect the memory failure
Describe the Pod:
kubectl describe pod memory-limit-pod-lab -n resource-lab
Look for container state and termination information. You may see a reason such as OOMKilled, which means the container was killed because it exceeded the allowed memory.
Check logs if available:
kubectl logs memory-limit-pod-lab -n resource-lab
Step 12: Delete the memory test Pod
Delete the memory test Pod:
kubectl delete -f memory-limit-pod-lab.yaml
Step 13: Check node resource information
Describe the node:
kubectl describe node stealth-k8s-lab-control-plane
Look for sections such as:
- Capacity
- Allocatable
- Allocated resources
Capacity is the total resource reported by the node. Allocatable is the amount available for Pods after system reservations. Allocated resources shows what has been requested and limited by running workloads.
Step 14: Optional metrics note
You may see examples online using:
kubectl top pods kubectl top nodes
These commands require metrics-server or another metrics pipeline. A basic kind cluster may not have this installed by default.
We will cover metrics and observability later. For this lesson, focus on resource requests, limits, scheduling behavior, and describe output.
Step 15: Clean up the lab
Delete the standalone Pod:
kubectl delete -f resource-pod-lab.yaml
Delete the Deployment:
kubectl delete -f resource-deployment-lab.yaml
Delete the Namespace:
kubectl delete namespace resource-lab
Confirm cleanup:
kubectl get namespaces kubectl get pods -A
Common troubleshooting commands
Use this flow when a workload is stuck, unstable, or behaving like a resource problem:
kubectl get pods -n <namespace> kubectl describe pod <pod-name> -n <namespace> kubectl get pod <pod-name> -n <namespace> -o yaml kubectl describe node <node-name> kubectl get events -n <namespace> --sort-by=.metadata.creationTimestamp kubectl logs <pod-name> -n <namespace>
This usually tells you:
- Whether the Pod is Pending, Running, or restarting
- Whether the scheduler could place the Pod
- Whether the Pod requested too much CPU or memory
- Whether a container was killed for memory usage
- Whether the node has enough allocatable resources
Common resource mistakes
No requests set
Kubernetes can run the workload, but the scheduler has less useful information about how much resource the workload needs.
Requests set too high
Pods may remain Pending because no node can satisfy the requested resources.
Limits set too low
Applications may be throttled or killed even though they are working normally.
Memory limit too close to normal usage
Normal application spikes can cause the container to exceed its limit and be killed.
Assuming CPU and memory behave the same
CPU is usually throttled. Memory overuse can terminate the container.
Where this fits in real clusters
In real environments, resource requests and limits usually connect to broader controls:
- ResourceQuotas: Limit total resource usage in a Namespace.
- LimitRanges: Apply defaults and boundaries for resource requests and limits.
- Horizontal Pod Autoscaling: Scale workloads based on metrics.
- Cluster Autoscaling: Add or remove nodes based on scheduling needs.
- Monitoring: Track actual CPU, memory, restarts, throttling, and saturation.
- Capacity planning: Make sure clusters have enough room for normal and peak workloads.
We will cover several of these topics later. For now, focus on the core idea: requests help scheduling, and limits define runtime boundaries.
What you just learned
In this lesson, you:
- Created a Namespace for resource testing
- Created a Pod with CPU and memory requests and limits
- Created a Deployment with resource settings
- Inspected resource settings with describe and YAML output
- Created an unschedulable Pod on purpose
- Used events to understand a Pending Pod
- Created a memory limit failure on purpose
- Learned why memory limits can kill containers
- Inspected node capacity and allocatable resources
- Cleaned up the lab resources
Check your understanding
Use these questions to test whether the resource concepts made sense.
- What does a resource request tell Kubernetes?
- What does a resource limit define?
- Which resource setting is most important for scheduling?
- What does 100m CPU mean?
- What can happen if a Pod requests more CPU than any node can provide?
- What can happen if a container exceeds its memory limit?
- What usually happens when a container hits its CPU limit?
- Are resource requests and limits set per Pod or per container?
- What command shows resource details and scheduling events for a Pod?
- What node sections help you understand resource capacity?
- Why might kubectl top not work in a basic kind cluster?
- What are three common controls related to resources in real clusters?
Answer key
- It tells Kubernetes how much CPU or memory to reserve for scheduling.
- It defines the maximum amount of a resource the container is allowed to use.
- Requests.
- One tenth of a CPU core.
- The Pod can remain Pending because the scheduler cannot place it.
- It can be killed, often with a reason such as OOMKilled.
- It is usually throttled.
- They are set per container.
- kubectl describe pod <pod-name>.
- Capacity, Allocatable, and Allocated resources.
- Because metrics-server or a metrics pipeline may not be installed.
- Examples: ResourceQuotas, LimitRanges, autoscaling, monitoring, and capacity planning.