For most of Kubernetes' history, a pod's resource requests and limits were fixed for its lifetime. To give a container more memory you changed the Deployment, and Kubernetes replaced the pod. For stateless web servers that is fine. For a JVM that takes three minutes to warm its caches, a stateful service with long-lived client connections, or a job halfway through a two-hour computation, a restart is expensive.
In-place pod resize removes that restriction: you can change CPU and memory on a running pod, and the kubelet adjusts the container's cgroup limits, often without restarting anything. The feature graduated to beta, enabled by default, in Kubernetes 1.33, and has been maturing since. This post explains how it works and how we use it.
The basic operation
Resizing uses a dedicated resize subresource on the pod. Changing spec.containers[].resources
through a normal update is rejected; going through the subresource is what marks the change as an
intentional resize.
kubectl patch pod report-worker-0 --subresource resize --patch '
{"spec":{"containers":[{"name":"worker","resources":{
"requests":{"cpu":"2","memory":"6Gi"},
"limits":{"cpu":"4","memory":"6Gi"}}}]}}'
kubectl needs to be version 1.32 or newer for --subresource resize. The pod spec now shows the
desired resources, and the pod status shows what the kubelet has actually applied:
kubectl get pod report-worker-0 -o jsonpath='{.status.containerStatuses[0].resources}'
{"limits":{"cpu":"4","memory":"6Gi"},"requests":{"cpu":"2","memory":"6Gi"}}
When the two match, the resize is complete.
Resize status: pending and in progress
Not every resize can happen immediately. The pod reports progress through conditions:
| Condition | Reason | Meaning |
|---|---|---|
PodResizePending |
Deferred |
Not possible right now (node lacks free capacity), kubelet will retry |
PodResizePending |
Infeasible |
Cannot ever happen on this node (more than node capacity) |
PodResizeInProgress |
Kubelet accepted the resize and is applying it |
kubectl get pod report-worker-0 -o jsonpath='{.status.conditions[?(@.type=="PodResizePending")]}'
A Deferred resize is a useful signal: the node is too full. The pod keeps running with its old
resources, and the resize happens if capacity frees up. Kubernetes does not move the pod to another node
for you; that is a job for an autoscaler or for you.
resizePolicy: restart or not
Each container can declare, per resource, whether a resize requires a container restart:
spec:
containers:
- name: worker
image: registry.example.com/report-worker:4.2.0
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired
- resourceName: memory
restartPolicy: RestartContainer
resources:
requests: { cpu: "1", memory: "4Gi" }
limits: { cpu: "2", memory: "4Gi" }
NotRequired(the default) applies the new value to the running container. CPU changes are almost always safe this way: the cgroup quota changes and the process simply gets more or less CPU time.RestartContainerrestarts the container to apply the change. This is right for applications that size themselves at startup, such as a JVM with a heap based on container memory, or a database with a fixed buffer pool size. The restart is a container restart in place, not a new pod: same node, same IP, same volumes.
Memory decreases are special
Increasing memory limits is easy. Decreasing them below what the process currently uses would trigger
an out-of-memory kill. The kubelet checks current usage and only applies a memory limit decrease when it
is safe, otherwise it keeps the resize in progress. Applications rarely give memory back to the operating
system on their own, so in practice memory decreases work best with RestartContainer.
QoS class does not change
A pod's quality of service class (Guaranteed, Burstable, BestEffort) is fixed at creation. A resize that would change it is rejected. For example, a Guaranteed pod (requests equal limits for every resource) must stay Guaranteed: you can raise both request and limit together, but not just one. A Burstable pod cannot become Guaranteed through resize. Plan the QoS class you want before creating the workload.
What can and cannot be resized
| Can resize | Cannot resize (at the time of writing) |
|---|---|
| CPU and memory requests and limits of regular containers | Other resources such as GPUs, ephemeral storage |
| Sidecar containers (restartable init containers) in recent versions | Non-restartable init containers |
| Pods on Linux nodes with cgroup v2 | Windows pods |
Check the documentation for your exact cluster version; the list has been growing with each release.
Deployments and controllers
Resizing a pod directly does not change its controller's template. If the Deployment's pod template still says 1 CPU, any new pod, after a rollout, a node drain or a crash, gets 1 CPU again. That is the intended design: in-place resize is a runtime adjustment, the template remains the declared baseline.
In practice there are two patterns:
- Permanent change: update the Deployment template as usual. New pods get the new values through a normal rollout. In-place resize is not involved.
- Temporary or automatic change: something resizes running pods on demand, such as an operator, a script responding to a workload pattern, or the Vertical Pod Autoscaler.
Vertical Pod Autoscaler
VPA has always been limited by restarts: to apply a recommendation it had to evict pods. VPA has added an update mode that applies recommendations in place where possible, falling back to eviction when an in-place resize is not possible. Check the VPA release notes for the mode name and status in your version. This combination is the most useful application of the feature: pods converge to right-sized resources without the churn of evictions.
Where we use it
- Report workers. A StatefulSet of workers processes large monthly reports. On the first days of the month we resize CPU up in place for the duration of the run, then back down. No restarts, no lost progress.
- JVM services during incidents. When a service is CPU-starved during a traffic spike, raising CPU
in place takes seconds, much faster than a rollout of new pods that each need to warm up. Memory changes
for these services use
RestartContainer, because the heap size is computed at startup. - Right-sizing experiments. Before changing a template permanently, we resize one pod and watch its latency and throttling metrics for a day.
Things to watch
- Node capacity. Resize does not reschedule. On full nodes, resizes stay
Deferred. Autoscalers that react to pending pods do not see deferred resizes as demand, so keep headroom or handle the condition yourself. - CPU throttling metrics. After a CPU limit increase,
container_cpu_cfs_throttled_periods_totalshould fall. If it does not, the application may be limited by something else. - Admission policies. Policy engines that validate resources on pod create may not see resizes; make
sure your policies also cover the
pods/resizesubresource if you enforce limits. - RBAC. Resizing needs
patchonpods/resize, which can be granted separately from general pod update rights.
Key takeaways
- In-place resize changes running pods' CPU and memory through the
resizesubresource, beta and on by default since Kubernetes 1.33. - Pod status and the
PodResizePendingandPodResizeInProgressconditions show what has actually been applied. resizePolicydecides per resource whether the container restarts; useRestartContainerfor applications that size themselves at startup.- QoS class cannot change, and controllers' templates are not updated by a resize.
- Combined with VPA, it lets pods converge to the right size without eviction churn.
References
- Kubernetes, Resize CPU and Memory Resources assigned to Containers: https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/
- KEP-1287, In-place Update of Pod Resources: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/1287-in-place-update-pod-resources
- Vertical Pod Autoscaler: https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler
Comments
0No comments yet. Be the first to comment!
Sign In to post a comment.