Kubernetes Operations

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
Field note: In shared clusters, workloads without resource requests are guesses. Kubernetes can run them, but the scheduler has less information to make good placement decisions.

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.

Field note: Memory and CPU behave differently. Memory overuse can kill a container. CPU overuse is usually 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.

Field note: Resource requests and limits are defined per container, not just per Pod. A Pod with multiple containers can have separate settings for each container.

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.

Field note: Pending Pods are often scheduler problems. The events section usually explains whether the issue is resource pressure, node selectors, taints, storage, or another scheduling constraint.

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
Field note: Memory limits are hard boundaries. If the container uses more memory than allowed, it can be killed.

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
Bottom line: Resource requests help Kubernetes schedule Pods. Resource limits help define how much a container can consume. Good resource settings make shared clusters more predictable and safer to operate.

Check your understanding

Use these questions to test whether the resource concepts made sense.

  1. What does a resource request tell Kubernetes?
  2. What does a resource limit define?
  3. Which resource setting is most important for scheduling?
  4. What does 100m CPU mean?
  5. What can happen if a Pod requests more CPU than any node can provide?
  6. What can happen if a container exceeds its memory limit?
  7. What usually happens when a container hits its CPU limit?
  8. Are resource requests and limits set per Pod or per container?
  9. What command shows resource details and scheduling events for a Pod?
  10. What node sections help you understand resource capacity?
  11. Why might kubectl top not work in a basic kind cluster?
  12. What are three common controls related to resources in real clusters?
Answer key
  1. It tells Kubernetes how much CPU or memory to reserve for scheduling.
  2. It defines the maximum amount of a resource the container is allowed to use.
  3. Requests.
  4. One tenth of a CPU core.
  5. The Pod can remain Pending because the scheduler cannot place it.
  6. It can be killed, often with a reason such as OOMKilled.
  7. It is usually throttled.
  8. They are set per container.
  9. kubectl describe pod <pod-name>.
  10. Capacity, Allocatable, and Allocated resources.
  11. Because metrics-server or a metrics pipeline may not be installed.
  12. Examples: ResourceQuotas, LimitRanges, autoscaling, monitoring, and capacity planning.
← Previous: Namespaces Series index Next lesson coming soon →