Part of the series Kubernetes basics

Kubernetes CPU and Memory Requests: Concepts first, then practice

Why can a Pod be Pending when the node looks idle? Understand how Kubernetes counts requests, work through one example, then use the commands to inspect and update your workload.

, 14 min read

Imagine a website called storefront. You want three copies of it running. Each copy runs in a Pod.

You give each Pod a large CPU and memory request. One Pod starts, but the other two stay Pending, which means they have not started running yet.

The node may look almost idle. Why are the other Pods waiting?

Kubernetes checks what the Pods have asked for, not just what they are using at that moment. Let’s understand that first, then fix a small practice workload.

Part 1: Understand the concepts

1. What are CPU and memory?

CPU does the work: processing requests, running calculations, and executing your application code. More available CPU can help an application finish CPU-intensive work faster.

Memory holds the information the running application is working with, such as loaded data and temporary objects.

A node is a machine with a limited amount of CPU and memory. Several Pods share those resources. A container runs the application inside a Pod.

Our example uses a Deployment, which keeps the requested number of application Pods running through ReplicaSets. Three replicas means three Pods. Each Pod needs enough room on one suitable node.

2. What does a request mean?

A request tells Kubernetes how much CPU or memory to account for when placing a container on a node.

For example, a container requests half a CPU and 500 MiB of memory. The scheduler, which chooses a node for the Pod, looks for a node with enough unallocated request budget.

Think of booking seats on a bus. A seat can be reserved even when nobody is sitting in it yet. In the same way, an idle Pod still counts toward the node’s requested resources.

This is a scheduling reservation, not a measurement of current usage. It does not mean that amount of memory is immediately filled or that CPU is always busy. Under CPU contention, requests also influence the relative CPU time containers receive.

An application can use more than its request when resources are available and its limits allow it. A memory request is not a hard boundary preventing the application from using more memory.

3. What does a limit mean?

A limit controls resource use while the container runs.

Resource What usually happens at the limit?
CPU CPU time is throttled. The program may run more slowly.
Memory The container can be killed for running out of allowed memory. Its status may show OOMKilled.

Whether a killed container restarts depends on its restart policy. Containers in an ordinary Deployment use Always, so Kubernetes attempts to restart them.

For a simple example:

Setting CPU Memory
Request 250m 256Mi
Limit 1000m 512Mi

The scheduler accounts for 250m CPU and 256Mi memory. The container may use more, up to its limits, when resources are available. It does not automatically consume its full limit.

A request cannot be greater than the corresponding limit. If you set a limit without a request, Kubernetes may copy the limit into the request when no other admission default supplies one. Always inspect the Pod’s final settings.

4. Read CPU and memory units

CPU values describe amounts of CPU time:

Value Meaning
1 or 1000m One CPU.
500m Half a CPU.
250m A quarter of a CPU.

The m means millicpu: one thousandth of a CPU. A request of 500m does not usually give the container its own dedicated half-core; CPU time is shared.

For memory:

Value Meaning
1024Ki 1 MiB.
1024Mi 1 GiB.
1Gi 1024 MiB.

Mi and M are different: 500Mi is 524,288,000 bytes, while 500M is 500,000,000 bytes. Keep calculations in one unit. Also, lowercase memory m means millibytes, so it is not a substitute for Mi.

If node memory is 3891200Ki, divide by 1024 to get 3800Mi.

5. Capacity and allocatable are different

Capacity describes the node’s total resources.

Allocatable describes the resources Kubernetes makes available to Pods after configured system reservations and applicable eviction reservations.

For example, a machine may have 4 GiB of memory, but less than 4 GiB is allocatable to Pods. Use the value reported by the node; do not assume a fixed reservation percentage.

Node's total resources: Capacity
        ↓ subtract configured reservations
Resources available to Pods: Allocatable
        ↓ subtract requests already assigned
Request budget remaining for more Pods

The scheduler checks requests against allocatable. Do not use the full machine capacity to size your application.

6. Other Pods have requests too

Your website is usually sharing the node with other Pods. These may include networking components, monitoring agents, and other applications.

Their requests use part of the allocatable budget. System reservations already reflected in allocatable and requests belonging to system Pods are different things; do not subtract the system reservation twice.

For this example:

Budget for storefront = Allocatable − requests belonging to other Pods

When calculating a replacement budget for all three storefront replicas, leave their old requests out of the “other Pods” total. You are replacing those numbers, not adding a second permanent set.

During a rolling update, however, old and new Pods can temporarily exist together. That temporary overlap still needs room.

7. Why an idle-looking node can reject a Pod

Suppose a node has 2000m CPU allocatable. Existing Pods already request 1750m. Only 250m remains in its CPU request budget.

A new Pod requesting 500m will not fit, even if current CPU usage is just 100m.

Scheduling checks: 1750m already requested + 500m new request > 2000m
Result: this node cannot fit the new Pod

The Pod may show a FailedScheduling event with Insufficient cpu or Insufficient memory if no suitable node can fit it.

Pending alone does not prove a request problem. Taints, affinity rules, storage requirements, and other constraints can also prevent placement. Read the events first. The cluster also cannot combine half of one node and half of another to fit a single Pod.

8. Calculate a budget for three replicas

For this exercise, assume all three replicas must fit on one eligible worker node and each Pod has one application container.

Use these illustrative numbers, not values to copy into every cluster:

Step CPU Memory
Node allocatable 2000m 3800Mi
Requests from other Pods 750m 340Mi
Budget remaining 1250m 3460Mi
Extra headroom chosen for this exercise 200m 380Mi
Budget to divide 1050m 3080Mi
Divide by three 350m About 1026Mi
Rounded-down request per Pod 350m 1000Mi

The calculation is:

Per-Pod budget = (Allocatable − other requests − chosen headroom) ÷ replicas

Three Pods requesting 350m and 1000Mi reserve 1050m CPU and 3000Mi memory. Adding the other Pods gives 1800m CPU and 3340Mi memory, below the node’s allocatable resources.

Headroom is an extra choice for this exercise, not a Kubernetes rule that requires 10% or 15%. It leaves room for additional requests. It does not guarantee that total actual usage will stay safe.

This calculates what fits on paper. It does not prove what the application needs. Choose real requests using measured usage, expected load, and reliability needs. Making requests smaller does not make the application use less CPU or memory.

For example, if each replica needs 1400Mi to work reliably, assigning 1000Mi requests does not solve the capacity problem. You may need more capacity, fewer replicas if permitted, or an application change.

9. Count the whole Pod, including startup work

If a Pod contains an app and a regular sidecar, add their requests together for each resource:

App: 300m CPU + sidecar: 50m CPU = 350m Pod CPU request

A regular init container runs before the application containers. When there are only ordinary sequential init containers, compare:

  • The sum of the regular containers’ requests.
  • The largest ordinary init-container request.

Use the larger value for each resource separately. CPU and memory can therefore be dominated by different containers.

For example, the app requests 350m CPU, but an init container requests 1000m. The Pod still needs a 1000m CPU scheduling budget, even after that init container finishes.

Do not reduce an init container’s request blindly: it also needs enough resources to complete its work. Our tiny practice init container only prints a message and waits.

Native sidecars, Pod-level resource settings, and Pod overhead can change the full accounting. The lab below uses one ordinary init container, one app container, and no additional configured Pod overhead.

10. Changing a Deployment can temporarily need extra space

Editing resource requests in a Deployment changes its Pod template. Kubernetes then rolls out replacement Pods.

With the usual RollingUpdate settings, a rollout may create an extra Pod before removing an old one. On a crowded node, the extra Pod may not fit. Whether progress stops depends on the rollout settings and how many old Pods may become unavailable.

Possible approaches include adding capacity, allowing old Pods to stop before replacements start, or scaling down first when interruption is allowed.

Scaling to zero stops the whole application. It is a simple option for the disposable lab below because it frees all old reservations before creating replacements. It is not a required step for every resource change.

A StatefulSet also has a Pod template, but its update ordering and storage behavior differ. Plan a stateful workload’s changes separately; the Deployment lab is not a universal restart procedure.

11. Namespace policies can affect the result

A LimitRange can supply defaults and enforce allowed sizes or ratios. A ResourceQuota can cap the total requested or limited resources for a namespace.

These checks happen when resources are admitted. A policy may reject Pod creation, so you might see a ReplicaSet creation error rather than an existing Pending Pod.

Distinguish three questions:

  1. Was the Pod allowed to be created? Check policies and controller events.
  2. Can the Pod fit on an eligible node? Check requests and scheduling events.
  3. Does the running app have enough resources? Check usage, throttling, restarts, and application behavior.

Part 2: YAML, commands, and practice

1. Create the practice workload

Use a disposable cluster where you can create a namespace and Deployment. The following file asks for three Pods, each with a 1 CPU and 1500Mi request.

On a small cluster, some Pods may stay Pending. A larger cluster may fit them all; the outcome depends on your nodes and existing workloads.

Save this as storefront.yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: shop
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: storefront
  namespace: shop
spec:
  replicas: 3
  selector:
    matchLabels:
      app: storefront
  template:
    metadata:
      labels:
        app: storefront
    spec:
      initContainers:
        - name: prepare
          image: busybox:1.37.0
          command: ["sh", "-c", "echo preparing && sleep 2"]
          resources:
            requests:
              cpu: "1"
              memory: 1500Mi
      containers:
        - name: web
          image: nginx:stable
          resources:
            requests:
              cpu: "1"
              memory: 1500Mi
            limits:
              cpu: "2"
              memory: 2Gi

The example’s nginx:stable tag can change over time. Use a tested pinned version or digest when you need a repeatable image.

Confirm your cluster, then apply the file:

kubectl config current-context
kubectl apply -f storefront.yaml
kubectl get pods -n shop -l app=storefront -o wide

-n shop selects the namespace. -l app=storefront selects this app’s Pods. -o wide also shows their nodes.

2. Find why a Pod is waiting

Copy the name of a Pending Pod, then inspect it:

kubectl describe pod <pending-pod-name> -n shop

Read the Events section at the bottom. Look for FailedScheduling and its full message.

If it says Insufficient cpu or Insufficient memory, continue with the budget calculation. If it names a taint or another restriction, investigate that too. A Pod may fail several scheduling checks at once.

3. Read the eligible node’s allocatable resources

List nodes and inspect the worker you intend to use:

kubectl get nodes
kubectl get node <node-name> \
  -o jsonpath='{.status.allocatable}{"\n"}'

Replace <node-name> with its actual name. For example, CPU 2 means 2000m; memory 3891200Ki means 3800Mi.

Check the node’s labels and taints if not every node is eligible. A cluster-wide total is not enough for a single-node calculation.

4. Read the requests belonging to other Pods

kubectl describe node <node-name>

Find Non-terminated Pods and Allocated resources. The first lists Pods and requests; the second summarizes totals.

Count all assigned Pods that will remain, including system Pods. Leave out the old storefront Pods only when calculating the final budget that replaces them. Count assigned Pods, even if they are not Ready yet; do not subtract only those marked Running.

Using the illustrative numbers from Part 1:

CPU budget per replica:
(2000m − 750m − 200m) ÷ 3 = 350m

Memory budget per replica:
(3800Mi − 340Mi − 380Mi) ÷ 3 ≈ 1026Mi
Round down to 1000Mi.

Use your actual node values and your application’s needs. If the result is too small for the app, add capacity or reconsider the allowed replica count instead of forcing it to fit.

5. Check namespace policies

kubectl get limitrange,resourcequota -n shop
kubectl describe limitrange -n shop
kubectl describe resourcequota -n shop

If none exist, those checks have nothing to report. If they do exist, check defaults, minimums, maximums, and remaining quota before choosing new requests.

6. Stop the old Pods in this disposable lab

This step causes an outage. Use it only where stopping the practice app is acceptable. For an existing task, follow its availability requirements.

kubectl scale deployment/storefront -n shop --replicas=0
kubectl get pods -n shop -l app=storefront

Wait until the old storefront Pods have disappeared. Scaling to zero requests their removal; it does not guarantee that termination completes immediately.

Also change replicas to 0 in your local storefront.yaml before the next apply. Otherwise applying the old file would request three Pods again too early.

7. Update the file, then apply it

In storefront.yaml, change the requests under both prepare and web. Keep their image, command, and other fields.

If your calculation supports the example budget, use:

# Within each container's existing resources block:
requests:
  cpu: 350m
  memory: 1000Mi

The web container’s limits remain 2 CPU and 2Gi for this exercise. The smaller requests fit below them. Those limits are not reserved capacity, and they do not guarantee that every replica can use its maximum at the same time.

Apply the edited file while it still says replicas: 0:

kubectl apply -f storefront.yaml

For a direct update to the regular web container, an alternative command is:

kubectl set resources deployment/storefront -n shop -c web \
  --requests=cpu=350m,memory=1000Mi

That alternative updates the live Deployment. Inspect and update the init container separately; do not assume the named web update changes it. Keep the source YAML consistent so a later apply does not restore old values.

To inspect or edit the full live Pod template:

kubectl get deployment/storefront -n shop -o yaml
kubectl edit deployment/storefront -n shop

Resource settings are under spec.template.spec.containers[].resources and spec.template.spec.initContainers[].resources.

8. Bring back three replicas

In your local YAML, set replicas back to 3, then apply it:

kubectl apply -f storefront.yaml
kubectl rollout status deployment/storefront -n shop --timeout=120s

You can use kubectl scale deployment/storefront -n shop --replicas=3 instead, but update the local file too if you will keep using it.

A timeout means the rollout did not finish in that time. Inspect events and Pod status rather than assuming it will fix itself.

9. Verify the new Pods and their requests

kubectl get deployment/storefront -n shop
kubectl get pods -n shop -l app=storefront -o wide

The goal is three updated, available replicas. For this one-app-container example, each Pod should normally show 1/1 Ready after initialization.

Read the actual requests from the Pods:

kubectl get pods -n shop -l app=storefront \
  -o custom-columns='NAME:.metadata.name,INIT_CPU:.spec.initContainers[*].resources.requests.cpu,INIT_MEM:.spec.initContainers[*].resources.requests.memory,APP_CPU:.spec.containers[*].resources.requests.cpu,APP_MEM:.spec.containers[*].resources.requests.memory'

Check that each Pod has the intended values for both containers. Then inspect the node’s Allocated resources section again:

kubectl describe node <node-name>

Requests should fit within allocatable. Limits can total more than allocatable because they are not all reserved by the scheduler.

For current usage, if your cluster provides the resource metrics API:

kubectl top node <node-name>
kubectl top pod -n shop --containers

These show recent usage, not scheduling requests. Metrics Server commonly supplies this API. An unavailable metrics API does not by itself mean your resource settings are wrong.

Finally, check the application itself. A Ready Pod is useful evidence, but appropriate probes and a real application check are needed to establish that it serves users correctly.

10. Troubleshoot with the right question

What you see What to investigate
Pending with insufficient CPU or memory Effective Pod requests, init containers, eligible nodes, and remaining allocatable budget.
New Pods waiting during a rollout Old and new Pods overlapping, rollout strategy, and available capacity.
No new Pod object appears Deployment and ReplicaSet events, quota, LimitRange, and admission errors.
Request greater than limit Correct that resource’s request or limit according to the task.
OOMKilled after startup Real memory needs, memory limit, application behavior, and node memory pressure.
App is slow with a CPU limit CPU demand and evidence of throttling.
All Pods run but the node is under pressure Actual usage can exceed requests; review sizing and capacity.

Useful inspection commands:

kubectl describe deployment/storefront -n shop
kubectl get replicasets -n shop -l app=storefront
kubectl describe pod <pod-name> -n shop
kubectl get events -n shop --sort-by=.metadata.creationTimestamp

Before finishing, make sure you can explain what the app needs, what it requests, what its limits allow, and why its Pod fits on a particular node.

11. Remove the practice resources

If you created shop only for this exercise and no longer need anything in it:

kubectl delete namespace shop

This removes all resources in that namespace, not only storefront.

Official references