Kubernetes 1.35: In-Place Pod Resize is GA — Scale Vertically Without Restarting
Update CPU and memory of running Pods without recreating them
The Problem: “Simple” Changes That Cause Disruptions
A production service shows CPU throttling. P95 latency is rising. The solution is obvious: increase the CPU limit from 500m to 700m.
In earlier versions of Kubernetes, this “simple change” triggered a cascade of unwanted events:
# Change this...
resources:
limits:
cpu: "500m"
# ...to this
resources:
limits:
cpu: "700m"
Consequences in K8s ≤1.34:
- Pod completely recreated
- Active connections terminated
- In-memory cache lost
- Local state deleted
- Jobs in progress interrupted
No code changed. No behavior changed. Just a number adjustment. But the system treated that adjustment as a full deployment.
Kubernetes 1.35 changes this fundamentally.
The Solution: In-Place Resource Update (GA)
Starting with Kubernetes 1.35,in-place Pod resource updates are GA(Generally Available). This means CPU and memory can be modified in running Pods, and the node applies the new values without the automatic recreation cycle.
Key features:
- CPU changes applied hot (without restart)
- Memory changes configurable (with or without container restart)
- Pod is never recreated — only cgroups are adjusted
- State, connections, and cache preserved
- Observable via Pod status and conditions
Architecture: How Resize Works
The process involves several Kubernetes components working in coordination:
The New Mental Model: Desired vs Actual vs Allocated
Kubernetes 1.35 introduces an important distinction in how resources are reported:
| Field | Description |
|---|---|
spec.containers[].resources |
What the operator*desires* |
status.containerStatuses[].allocatedResources |
What the node*reserved* |
status.containerStatuses[].resources |
What the container*is using* |
status.conditions |
State of the resize (Pending, InProgress) |
status.observedGeneration |
Confirmation that kubelet processed the change |
This visibility allows you to know exactly what state the resize is in at any moment.
Configuration: resizePolicy
The resize behavior is configured per resource type usingresizePolicyin the container spec:
Pod Spec with Resize Policy
apiVersion: v1
kind: Pod
metadata:
name: app-con-resize
spec:
containers:
- name: app
image: mi-app:latest
ports:
- containerPort: 8080
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired # CPU hot
- resourceName: memory
restartPolicy: RestartContainer # Memory with restart
resources:
requests:
cpu: "300m"
memory: "256Mi"
limits:
cpu: "300m"
memory: "256Mi"
Recommendation for production:
-
CPU:
NotRequired— CPU changes are safe to apply hot -
Memory:
RestartContainer— More predictable than waiting for the app to free memory
Demo: Verifying that Resize Works
To demonstrate that resize really works without restart, you can create a simple server that exposes its PID and current cgroup limits.
Demo Server (Go)
package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
funcread(path string) string {
b, err := os.ReadFile(path)
if err != nil {
return fmt.Sprintf("unavailable (%v)", err)
}
return strings.TrimSpace(string(b))
}
funchandler(w http.ResponseWriter, r *http.Request) {
pid := os.Getpid()
// cgroup v2 paths
cpuMax := read("/sys/fs/cgroup/cpu.max")
memMax := read("/sys/fs/cgroup/memory.max")
io.WriteString(w, fmt.Sprintf("pid=%d\n", pid))
io.WriteString(w, fmt.Sprintf("cpu.max=%s\n", cpuMax))
io.WriteString(w, fmt.Sprintf("memory.max=%s\n", memMax))
}
funcmain() {
http.HandleFunc("/", handler)
fmt.Println("listening on :8080")
http.ListenAndServe(":8080", nil)
}
Dockerfile
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY . .
RUN go build -o app .
FROM alpine:3.20
WORKDIR /app
COPY --from=build /src/app /app/app
EXPOSE 8080
ENTRYPOINT ["/app/app"]
Deploy and Verify
# Create the Pod
kubectl apply -f pod-resize-demo.yaml
# Port-forward to access
kubectl port-forward pod/app-con-resize 8080:8080 &
# Verify initial state
curl localhost:8080
# pid=7
# cpu.max=30000 100000
# memory.max=268435456
Running the Resize
In Kubernetes 1.35, resize is executed against the Pod’sresizesubresource:
Increase CPU (Without Restart)
kubectl patch pod app-con-resize --subresource resize --type merge -p ' { "spec": { "containers": [ { "name": "app", "resources": { "requests": { "cpu": "700m" }, "limits": { "cpu": "700m" } } } ] } }'
Verify It Worked
# The endpoint should show:
# - Same PID (no restart)
# - New cpu.max value
curl localhost:8080
# pid=7 <- SAME PID
# cpu.max=70000 100000 <- NEW LIMIT
# memory.max=268435456
# Confirm there was no restart
kubectl get pod app-con-resize -o jsonpath='{.status.containerStatuses[0].restartCount}'
# 0
If the PID remains the same and cpu.max changed, the in-place resize worked correctly.
Increase Memory (With Container Restart)
With theRestartContainerpolicy for memory:
kubectl patch pod app-con-resize --subresource resize --type merge -p ' { "spec": { "containers": [ { "name": "app", "resources": { "requests": { "memory": "512Mi" }, "limits": { "memory": "512Mi" } } } ] } }'
In this case:
-
restartCountwill increment - The PID will change
- But the Pod will NOT be recreated— volumes and networking are preserved
Observability During Resize
Kubernetes 1.35 provides visibility of the resize state via conditions:
kubectl describe pod app-con-resize
Relevant conditions:
-
PodResizePending— The resize was requested but not applied yet -
PodResizeInProgress— The kubelet is applying the change
# See the detailed state
kubectl get pod app-con-resize -o jsonpath='{.status.conditions}' | jq
```
This eliminates uncertainty. There's no longer any guessing whether the change was applied — the system reports it explicitly. --- ## Limitations to Consider The feature is powerful but has clear limits: | Limitation | Description | |------------|-------------| | **QoS Class** | Cannot change post-creation (Guaranteed/Burstable/BestEffort) | | **Init containers** | Do not support resize | | **Ephemeral containers** | Do not support resize | | **Sidecars** | Yes, support resize | | **Windows Pods** | Not supported | | **Memory decrease** | Best-effort without restart (the app must free memory) | | **Node constraints** | Some CPU/Memory managers may block changes | These limitations are part of what makes the feature safe. A feature that promises everything becomes dangerous. A feature that declares its limits is operable. --- ## Scheduler Protection A valid concern: what happens if the resize is pending but the scheduler assumes it's already applied?
Kubernetes prevents this by being conservative during incomplete resizes. When scheduling, it considers the **maximum** between:
- What is requested (desired)
- What is allocated (allocated)
- What is applied (actual)
This prevents overcommit based on changes that haven't yet completed. --- ## Operational Impact The most significant change is not technical — it's cultural.
**Before K8s 1.35:**
- Teams avoided resize until it was urgent
- Engineers over-provisioned to avoid touching resources afterward
- "Right-sizing" was a project, not a habit
- On-call delayed simple fixes out of fear of disruption
**With K8s 1.35:**
- CPU corrections without restart cost
- Faster iteration over resource configuration
- Response to throttling without maintenance window
- Resize becomes a normal operation, not an event
---
## Command Summary
```bash
# Apply CPU resize
kubectl patch pod POD_NAME --subresource resize --type merge -p ' { "spec": { "containers": [{ "name": "CONTAINER_NAME", "resources": { "requests": { "cpu": "NEW_VALUE" }, "limits": { "cpu": "NEW_VALUE" } } }] } }'
# Check resize status
kubectl describe pod POD_NAME | grep -A5 Conditions
# View current vs desired resources
kubectl get pod POD_NAME -o jsonpath='{.status.containerStatuses[0].resources}'
# Confirm no restart occurred
kubectl get pod POD_NAME -o jsonpath='{.status.containerStatuses[0].restartCount}'
```
`
***
## **Conclusion**
Kubernetes 1.35 solves a problem that should never have existed: the need to restart a process just because a resource limit was adjusted.
With in-place resize GA:
* **CPU**can be adjusted without any restart
* **Memory**can be configured for container restart (not Pod)
* **Full observability**of resize state
* **Protection**against overcommit during pending changes
Vertical scaling finally behaves like an adjustment, not a deployment.
***
## **Resources**
* [KEP-1287: In-Place Pod Vertical Scaling](https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/1287-in-place-update-pod-resources)
* [Kubernetes 1.35 Release Notes](https://kubernetes.io/blog/)
* [Pod Resource Management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/)
***
*Published on yoDEV.dev — The Latin American developer community*
Top comments (0)