Back to Blog

In-Place Pod Resize for the CKAD Exam: The resize Subresource, resizePolicy, QoS Constraints & PodResizePending / PodResizeInProgress Conditions

A hands-on CKAD guide to in-place Pod vertical scaling (GA in Kubernetes 1.35) — how to change a running Pod's CPU and memory through the resize subresource without recreating it, what resizePolicy NotRequired vs RestartContainer really does, why the QoS class can never change, how to read PodResizePending (Infeasible vs Deferred) and PodResizeInProgress, and the limits around memory decreases, init containers, static CPU manager policies and Windows nodes.

By Sailor Team , October 13, 2026

For most of Kubernetes’ history, spec.containers[*].resources was immutable on a running Pod. If a container needed more memory, you edited the Deployment and accepted a rolling restart, or you let the Vertical Pod Autoscaler evict the Pod and recreate it. That rule changed. In-place Pod vertical scaling went GA in Kubernetes 1.35, and it adds a new mechanic the CKAD exam can legitimately test under its Application Environment, Configuration and Security domain: a dedicated resize subresource, a per-container resizePolicy, two new Pod conditions, and a set of hard rules about what a resize can and cannot do.

This guide is written from a practitioner’s perspective. We will resize a real Pod with kubectl patch --subresource resize, watch the difference between the desired resources in spec and the actual resources in status, deliberately trigger an Infeasible resize to see what the kubelet reports, and work through the constraints — QoS class, memory decreases, init containers, CPU manager policies — that turn into exam traps. Requests, limits, ResourceQuota and LimitRange are covered in our CKAD resource management guide; this article assumes those basics and goes one layer deeper.

Why In-Place Resize Exists

A Pod’s resource requests drive scheduling and a Pod’s limits drive cgroup enforcement. Before 1.33, both were fixed at creation. That created a well-known operational gap:

  • A JVM warms up and needs more memory than you estimated. The only fix is a restart, which throws away the warm-up.
  • A batch worker was over-provisioned. Reclaiming the CPU means killing the Pod.
  • The Vertical Pod Autoscaler could recommend better values but could only apply them by evicting the Pod.

In-place resize closes the gap. The kubelet can now update a running container’s cgroup limits directly, and the scheduler can account for the new requests on the node. Whether a container must restart to pick up the change is a per-resource, per-container decision you declare in the manifest.

Feature gateAlphaBeta (on by default)GA
InPlacePodVerticalScaling1.271.331.35
InPlacePodLevelResourcesVerticalScaling (Pod-level spec.resources)1.351.36—
InPlacePodVerticalScalingSchedulerPreemption1.37——
InPlacePodVerticalScalingMemoryBackedVolumes (resize emptyDir medium: Memory)1.37——
InPlacePodVerticalScalingExclusiveCPUs (static CPU manager + integer CPUs)1.32——

For the exam, the only row you need by heart is the first one. The others explain edge cases you may read about in the docs, which are allowed during the exam.

The Three Places Resources Now Live

The single most important mental model for this topic is that a resized Pod has three resource views, not one.

FieldMeaningWho writes it
spec.containers[*].resourcesThe desired requests and limits. Mutable through the resize subresource.You
status.containerStatuses[*].allocatedResourcesWhat the kubelet has admitted against the node. Primarily for internal scheduling accounting.kubelet
status.containerStatuses[*].resourcesWhat is actually configured on the running container (its cgroup). For a container that is not running, the values it will get on next start.kubelet

When you ask “did the resize happen?”, the answer is in status.containerStatuses[*].resources, not in spec. The spec reflects what you asked for the moment you patch it, even if the kubelet never grants the request.

The scheduler is conservative while a resize is unsettled: for a Pod with a pending or in-progress resize, it uses the highest of the desired, allocated and actual requests when deciding what still fits on the node. That prevents a node from being over-committed by a resize that has been requested but not yet applied.

resizePolicy: Deciding Whether a Container Restarts

Each container can declare a resizePolicy — a list of (resourceName, restartPolicy) pairs:

apiVersion: v1
kind: Pod
metadata:
  name: resize-demo
  namespace: qos-example
spec:
  containers:
  - name: app
    image: registry.k8s.io/pause:3.9
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: RestartContainer
    resources:
      requests:
        cpu: "700m"
        memory: "200Mi"
      limits:
        cpu: "700m"
        memory: "200Mi"

The two allowed values:

restartPolicy (resize)BehaviourTypical use
NotRequired (default when unspecified)The kubelet changes the cgroup live; the container keeps running and restartCount does not change.CPU almost always; memory for runtimes that read limits dynamically.
RestartContainerThe kubelet restarts that container (not the Pod) to apply the new value.Memory for JVMs, Go programs with GOMEMLIMIT baked at start, anything that sizes heaps once at boot.

Three rules follow directly from the semantics:

  1. If only CPU changes, the CPU policy decides. With NotRequired, the resize is applied without a restart.
  2. If only memory changes, the memory policy decides. With RestartContainer, the container restarts.
  3. If both change in one patch, a restart happens if either affected resource’s policy is RestartContainer. In the manifest above, patching CPU and memory together restarts the container because of the memory policy.

One validation rule the exam likes: a Pod whose top-level restartPolicy is Never cannot carry a resizePolicy of RestartContainer on any container. Such a Pod would have no way to bring the container back, so the API server rejects the combination. Every container in a restartPolicy: Never Pod must use NotRequired.

Don’t confuse this restartPolicy with the Pod-level spec.restartPolicy or with the newer container-level restartPolicy / restartPolicyRules used for sidecars and per-container restart behaviour (ContainerRestartRules, beta in 1.35). Three fields share one name; the one that lives inside resizePolicy only governs resizes.

Performing a Resize: The resize Subresource

Here is the part that catches people who have memorised kubectl edit pod. A plain kubectl edit pod resize-demo and changing resources fails with the familiar immutability error, because the main Pod endpoint still treats resources as immutable. The change must go through the resize subresource, and that needs kubectl v1.32 or newer (older clients return invalid subresource).

kubectl create namespace qos-example
kubectl apply -f resize-demo.yaml

# Resize CPU only: 700m -> 800m
kubectl patch pod resize-demo -n qos-example --subresource resize --patch \
  '{"spec":{"containers":[{"name":"app","resources":{"requests":{"cpu":"800m"},"limits":{"cpu":"800m"}}}]}}'

The other supported entry points all take the same flag:

# Interactive edit against the subresource
kubectl edit pod resize-demo -n qos-example --subresource resize

# Declarative, server-side
kubectl apply -f resize-demo.yaml --subresource resize --server-side

Verify what happened — and look at status, not just spec:

kubectl get pod resize-demo -n qos-example -o json | jq '{
  desired:  .spec.containers[0].resources,
  actual:   .status.containerStatuses[0].resources,
  allocated: .status.containerStatuses[0].allocatedResources,
  restarts: .status.containerStatuses[0].restartCount,
  conditions: [.status.conditions[] | select(.type | startswith("PodResize"))]
}'

After the CPU-only patch you should see desired, actual and allocated all at 800m and restartCount still 0. Now patch memory:

kubectl patch pod resize-demo -n qos-example --subresource resize --patch \
  '{"spec":{"containers":[{"name":"app","resources":{"requests":{"memory":"300Mi"},"limits":{"memory":"300Mi"}}}]}}'

Because the memory policy is RestartContainer, restartCount increments to 1 and the container comes back with 300Mi. The Pod object itself — its UID, IP, node and volumes — is unchanged. That is the whole value of the feature: a container restart in place rather than a Pod replacement.

What about Pods owned by a Deployment?

The subresource works on any Pod, including one managed by a ReplicaSet. The resize is applied to that Pod instance, but the Pod template in the Deployment is untouched, so the next rollout, scale-up or Pod replacement reverts to the template values. For a durable change, edit the Deployment template as usual (which triggers a normal rolling update, covered in our Deployments and rolling updates guide). In the exam, read the task carefully: “without restarting the Pod” or “without recreating the Pod” means the subresource; “update the Deployment so that new Pods get…” means the template.

Reading the Two Resize Conditions

Two Pod conditions were added for this feature. Both appear under status.conditions next to PodScheduled, Initialized, ContainersReady and Ready, and they are absent when no resize is outstanding.

ConditionStatus True meansreason values
PodResizePendingA resize was requested but the kubelet cannot grant it yet.Infeasible, Deferred
PodResizeInProgressThe kubelet accepted the resize and allocated the resources, but actuation (cgroup update, container restart) is still running.Error if actuation hit a problem (details in message)

The distinction between the two PodResizePending reasons matters:

  • Infeasible — the resize can never fit on this node. The classic cause is requesting more CPU or memory than the node’s allocatable capacity. The kubelet does not retry. Fix it by patching feasible values, or by moving the workload to a bigger node.
  • Deferred — the node is currently too full, but the request might fit later (for example after another Pod terminates or scales down). The kubelet retries periodically.

When several deferred resizes are waiting on the same node, the kubelet retries them in a defined order: higher PriorityClass first; at equal priority, Guaranteed Pods before Burstable; otherwise the resize that has been waiting longest. A high-priority resize that is deferred again does not block the others from being retried. Starting in 1.37, the alpha gate InPlacePodVerticalScalingSchedulerPreemption lets kube-scheduler watch for Deferred resizes and preempt lower-priority Pods on the node to free capacity — the same preemption machinery described in our Pod priority and preemption guide, now applied to resizes.

Forcing an Infeasible resize on purpose

The fastest way to internalise the conditions is to break one:

# Ask for 1000 CPUs (not 1000m) — no node has that
kubectl patch pod resize-demo -n qos-example --subresource resize --patch \
  '{"spec":{"containers":[{"name":"app","resources":{"requests":{"cpu":"1000"},"limits":{"cpu":"1000"}}}]}}'

kubectl get pod resize-demo -n qos-example -o jsonpath='{range .status.conditions[?(@.type=="PodResizePending")]}{.reason}{"\t"}{.message}{"\n"}{end}'

You will get something like Infeasible Node didn't have enough capacity: cpu, requested: 1000000, capacity: 8000. Now inspect the three views again: spec shows 1000 (your desire), status.containerStatuses[0].resources still shows 800m (reality), and restartCount is unchanged. Nothing was applied, nothing was restarted, and the Pod kept serving. Patching a sensible value clears the condition.

Note that a unit slip — "1000" instead of "1000m" — is one of the most common ways candidates produce exactly this condition by accident. If a resize task “doesn’t work”, check PodResizePending before anything else.

The Rules You Cannot Break

The feature is deliberately constrained. Each of the following is a legitimate exam distractor.

Only CPU and memory

Only cpu and memory can be resized in place. Extended resources, ephemeral-storage, hugepages and device plugin resources are not resizable. (The 1.37 alpha gate for memory-backed emptyDir sizeLimit is the one exception, and it requires cgroup v2 on the node.)

The QoS class is frozen at creation

A Pod’s QoS class (Guaranteed, Burstable, BestEffort) is computed once, when the Pod is created, and a resize must keep it the same:

QoS at creationWhat a resize may not do
GuaranteedMake requests differ from limits for CPU or memory. Requests must stay equal to limits.
BurstableMake requests equal limits for both CPU and memory at once (that would look like Guaranteed).
BestEffortAdd any requests or limits at all.

Related: once a request or limit exists, it cannot be removed by a resize, only changed. So you cannot “resize” a Burstable Pod into BestEffort by deleting its requests. The QoS section of the resource management guide explains how the class is computed; the resize rule is simply “it never changes afterwards”.

Memory decreases are best-effort

Lowering a memory limit below current usage would normally cause an OOM kill. With a NotRequired policy, the kubelet makes a best-effort check: if the container’s memory usage is above the new limit, the decrease is skipped and the resize stays PodResizeInProgress rather than killing the container. There is a documented race (usage can rise between the check and the cgroup write), so treat memory decreases as “probably safe”, not guaranteed. If a memory shrink absolutely must take effect, use RestartContainer for memory so the container restarts into the smaller limit.

Related gotcha: on nodes with swap enabled, memory requests cannot be resized unless the memory resizePolicy is RestartContainer.

Which containers can be resized

Container typeResizable in place?
Regular app containerYes
Sidecar (init container with restartPolicy: Always)Yes
Regular (non-restartable) init containerNo — it already ran to completion
Ephemeral container (kubectl debug)No

The multi-container Pod patterns guide covers the sidecar distinction; the short version is that only containers that are still running (or will run again) can meaningfully be resized.

Node and platform limits

  • Windows nodes are not supported.
  • Pods managed under the kubelet’s static CPU manager policy or static Memory manager policy cannot be resized in place (exclusive CPUs are pinned at admission). The alpha InPlacePodVerticalScalingExclusiveCPUs gate is the start of lifting that for Guaranteed Pods with integer CPU requests.
  • Disk-backed emptyDir volumes and persistent volumes cannot be resized through /resize — volume growth is a StorageClass allowVolumeExpansion matter, covered under CKAD volumes.

How It Interacts With Everything Else You Know

ResourceQuota and LimitRange. A resize is admitted like any resource change: if the new requests would exceed the namespace quota, the API server rejects the patch with the usual exceeded quota message. LimitRange max/min constraints also apply. Resizes do not get a free pass around namespace policy.

Scheduling and node capacity. A resize never moves a Pod. It either fits on the current node or it becomes Infeasible/Deferred. If you genuinely need a different node, the answer is still delete-and-recreate (or let the controller do it).

VPA. Classic VPA in Auto mode evicts Pods to apply recommendations. Newer VPA versions add an in-place update mode that uses this same subresource and falls back to eviction when a resize is infeasible. For the exam, the concept to carry is: VPA is the policy (“what should the values be?”); in-place resize is the mechanism (“apply them without recreating”).

Observed generation. With PodObservedGenerationTracking, status.observedGeneration tells you the latest metadata.generation the kubelet has acknowledged, and each resize condition carries its own observedGeneration. If status.observedGeneration lags metadata.generation, the kubelet has not processed your patch yet — useful when a resize “does nothing” and you are not sure whether the request even arrived.

Pod-level resources. Kubernetes 1.34+ Pods can declare spec.resources at the Pod level (shared across containers). Those can also be resized in place through the same subresource (InPlacePodLevelResourcesVerticalScaling, beta in 1.36). The container-level rules above still apply to each container.

An Exam-Style Walkthrough

The Pod api-cache in namespace shop is running out of memory. Increase its memory request and limit from 256Mi to 512Mi without recreating the Pod. The container must not be restarted during the change.

Work the task in this order:

# 1. Inspect the current policy and QoS
kubectl get pod api-cache -n shop -o jsonpath='{.spec.containers[*].name}{"\n"}{.spec.containers[*].resizePolicy}{"\n"}{.status.qosClass}{"\n"}'

If resizePolicy is empty, the default for memory is NotRequired, so the resize will not restart the container — exactly what the task wants. If it says RestartContainer for memory, you cannot satisfy “must not be restarted” with a resize; read the task again, because it may expect you to note that limitation.

Check QoS: if the Pod is Guaranteed, you must change request and limit together to the same value. If it is Burstable, you may raise just the limit — but do not accidentally make CPU and memory requests equal their limits simultaneously.

# 2. Resize through the subresource
kubectl patch pod api-cache -n shop --subresource resize --patch \
  '{"spec":{"containers":[{"name":"cache","resources":{"requests":{"memory":"512Mi"},"limits":{"memory":"512Mi"}}}]}}'

# 3. Prove it applied, with no restart
kubectl get pod api-cache -n shop -o jsonpath='{.status.containerStatuses[0].resources.limits.memory}{"\t"}{.status.containerStatuses[0].restartCount}{"\n"}'
kubectl get pod api-cache -n shop -o jsonpath='{.status.conditions[?(@.type=="PodResizePending")].reason}{"\n"}'

A clean result prints 512Mi 0 and an empty line for the condition. If the condition prints Infeasible, the node lacks headroom; if the task allows, pick a lower value, otherwise report the constraint. If it prints Deferred, wait a moment and re-check — another Pod may be terminating.

Budget about two minutes for a task like this. The expensive mistake is spending five minutes fighting kubectl edit pod on the main resource before remembering the subresource flag.

Quick Reference

  • Feature: InPlacePodVerticalScaling — alpha 1.27, beta 1.33, GA 1.35.
  • Entry point: kubectl patch|edit|apply … --subresource resize (kubectl ≥ 1.32).
  • Desired vs actual: spec.containers[].resources vs status.containerStatuses[].resources; allocatedResources is the kubelet’s admission view.
  • resizePolicy[].restartPolicy: NotRequired (default, live cgroup change) or RestartContainer (restarts that container only). restartPolicy: Never Pods must use NotRequired everywhere.
  • Conditions: PodResizePending (Infeasible = never on this node, no retry; Deferred = retried) and PodResizeInProgress (reason: Error on actuation failure).
  • Cannot: change QoS class, remove requests/limits, resize non-restartable init or ephemeral containers, resize on Windows or under static CPU/Memory manager policies, resize anything but CPU and memory.
  • Memory decreases are best-effort with NotRequired; with swap, memory requests need RestartContainer.
  • Deferred retry order: priority → Guaranteed before Burstable → longest waiting. Scheduler preemption for deferred resizes is alpha in 1.37.

Practice This Under Exam Conditions

The concepts above fit on one page; the skill the exam measures is doing it fast in a terminal while the clock runs. Create a Guaranteed Pod and try to turn it Burstable with a resize, watch the validation error, then fix it. Request 1000 CPUs and read the Infeasible message. Set memory to RestartContainer and confirm restartCount ticks while the Pod IP stays the same. Then do it all again from a blank terminal.

Sailor.sh’s Certified Kubernetes Application Developer (CKAD) Mock Exam Bundle runs performance-based scenarios in a live browser terminal, with the same time pressure and task phrasing as the real exam, so resource-management tasks like this one become reflex rather than recall. Pair it with the CKAD kubectl cheat sheet for the jsonpath expressions above, and with our free CKAD practice guide if you are building a local cluster to rehearse on.

Frequently Asked Questions

Can I change a running Pod’s CPU and memory without restarting it?

Yes, on Kubernetes 1.33+ (GA in 1.35). Patch the Pod through the resize subresource — kubectl patch pod NAME --subresource resize --patch '…' — and make sure the container’s resizePolicy for that resource is NotRequired (the default). The kubelet updates the container’s cgroup live and restartCount does not change.

Why does kubectl edit pod still say resources are immutable?

Because the main Pod endpoint still treats resources as immutable. Resizes must target the subresource: kubectl edit pod NAME --subresource resize. You also need kubectl v1.32 or newer, or you get an invalid subresource error.

What is the difference between PodResizePending Infeasible and Deferred?

Infeasible means the resize cannot fit on the Pod’s current node at all (for example, more CPU than the node has), and the kubelet will not retry — you must patch a feasible value. Deferred means it could fit later once other Pods free capacity, and the kubelet retries periodically.

Can an in-place resize change a Pod’s QoS class?

No. The QoS class is fixed when the Pod is created. A Guaranteed Pod must keep requests equal to limits, a Burstable Pod cannot be resized so that both CPU and memory requests equal their limits, and a BestEffort Pod cannot gain requests or limits. Requests and limits also cannot be removed once set.

Does resizing a Pod update its Deployment?

No. The resize applies only to that Pod instance; the Deployment’s Pod template is unchanged, so replacement Pods revert to the template values. For a lasting change, edit the Deployment template, which triggers a normal rolling update.

Can I resize init containers or ephemeral containers?

Sidecar containers (init containers with restartPolicy: Always) can be resized. Regular init containers that have already completed and ephemeral debug containers cannot.

Is lowering a memory limit safe?

With resizePolicy NotRequired, the kubelet makes a best-effort check and skips the decrease if current usage exceeds the new limit, leaving the resize in progress rather than OOM-killing the container. A small race remains. If the shrink must take effect, set the memory policy to RestartContainer.

Conclusion

In-place Pod resize replaces one of Kubernetes’ oldest “delete and recreate” rules with a precise, well-constrained mechanism. For the CKAD, carry these four things: the change goes through the resize subresource; resizePolicy decides whether a container restarts; status.containerStatuses[].resources is the truth about what is applied; and the QoS class, the set of resizable resources, and the resizable container types never change. Add the two conditions — PodResizePending with Infeasible versus Deferred, and PodResizeInProgress — and you can both perform a resize and explain why one did not happen. Practise it a few times against a live cluster and the task becomes a two-minute win on exam day.

Limited Time Offer: Get 80% off all Mock Exam Bundles | Sale ends in 7 days. Start learning today.

Claim Now