Skip to main content

Kubernetes 1.36 In-Place Pod Resize Goes Beta: What Restart-Free Vertical Scaling Replaces in a PaaS Autoscaler

8 min readDora NodaDora Noda
Share
On this page

For the entire history of Kubernetes, changing a pod's CPU or memory envelope meant killing the pod. Every right-sizing — a tenant bumping past their plan's default limits, a recommender noticing chronic OOMKills, an autoscaler reacting to a traffic spike — ended the same way: evict, reschedule, cold-start. Kubernetes 1.36 closes that era for the pod as a whole: in-place vertical scaling for pod-level resources graduated to Beta in April 2026, enabled by default, following pod-level resources (beta in 1.34) and container-level in-place resize (GA in 1.35). The kubelet can now grow or shrink a running pod's aggregate resource budget with a single API write, usually without restarting a single container.

The artifact up front: here is the whole operation. A pod defined with a 2-CPU shared pool gets doubled to 4 CPUs while it keeps serving traffic:

bash
kubectl patch pod shared-pool-app --subresource resize --patch \
  '{"spec":{"resources":{"limits":{"cpu":"4"}}}}'

No new pod name, no rescheduling, no RESTARTS counter increment — the pod keeps its identity and its connections while the kubelet rewrites the cgroup limits underneath it. The rest of this post covers how that write is actually applied, what it replaces inside a PaaS autoscaler, and the beta caveats to clear before wiring it into production.

How the kubelet actually applies a resize

Patching the resize subresource only updates desired state. The kubelet then runs an admission and actuation sequence that operators need to understand, because every step has an observable failure mode.

1. Feasibility check against node allocatable. Before touching any cgroup, the kubelet verifies the new aggregate request fits in the node's remaining allocatable capacity. If the node is overcommitted, the resize is not silently dropped: the PodResizePending condition reports Deferred or Infeasible, telling you exactly why the envelope hasn't grown. This is the first thing to internalize — a resize is an admission decision made by the kubelet against local headroom, not by the scheduler against the cluster. The scheduler placed your pod at its old size and does not re-evaluate placement when it grows, which means aggressive in-place growth can fragment scheduler accounting on a densely packed node.

2. Ordered cgroup sequencing. To avoid transient overshoot, the kubelet updates cgroups in a fixed order: when scaling up, the pod-level cgroup expands first to create room, then individual container cgroups grow into it; when scaling down, container cgroups are throttled first and only then does the aggregate pod cgroup shrink. Expand-then-fill on the way up, drain-then-shrink on the way down.

3. Observable status, not a black box. Beta-grade observability rides on pod conditions and two status fields: status.allocatedResources (what the node admitted) versus status.resources (what is actually applied to the cgroups). While those two disagree, PodResizeInProgress reads True. A PaaS can — and should — surface these conditions in tenant-facing status instead of making users guess whether their plan bump took effect.

4. Per-container restart policy still rules. There is no resizePolicy at the pod level; the kubelet defers to each container's own policy. A container with NotRequired gets a live cgroup update via the CRI's UpdateContainerResources call; a container with RestartContainer is restarted to apply the new boundary safely. When pod-level limits grow, every container inheriting from the shared pool is evaluated this way — one RestartContainer sidecar in the pod means that sidecar bounces even though the main app doesn't.

What this replaces in a PaaS autoscaler: a before/after

The traditional vertical-autoscaling path in a git-push PaaS is restart-on-resize: the recommender (VPA in Auto mode, or homegrown logic) computes new requests, evicts the pod, and the workload controller recreates it at the new size. That path carries real costs — cold starts (JVM warmup, dropped connections, emptied caches), PodDisruptionBudget checks, and surge capacity so the replacement lands somewhere. In-place resize deletes most of that machinery for the common case:

Before: restart-on-resizeAfter: in-place actuation
Tenant bumps plan limitsEvict + recreate; brief unavailability per replicaPATCH /resize; pod keeps name, IP, connections
Recommender right-sizingNew rollout per adjustment; churn during convergenceCgroup rewrite; RESTARTS untouched
OOM-driven growthCrash, then recreate bigger (loses in-memory state twice)Grow the live pod before the next spike
Oversubscribed nodeScheduler finds a new node for the bigger podResize reports Infeasible; falls back to recreate
Autoscaler codeEviction orchestration, PDB handling, surge accountingResize call + condition watch + recreate fallback

Two honest limits keep this from being a universal replacement. First, a resize the node cannot fit still recreates — VPA's InPlaceOrRecreate update mode (alpha in VPA v1.5.0, available on GKE 1.34+ and recommended by AKS docs) exists precisely because the fallback path is permanent, not transitional: growth beyond node headroom always needs a new placement. Second, large corrections still bounce: field experience shows big initial right-sizes (e.g., cutting requests 10x on a long-mis-sized pod) exceed what the kubelet will apply live — memory the process is actually using can't be throttled away — so VPA falls back to recreation and only subsequent incremental adjustments go in-place. Expect the recreate path to handle the first correction and the in-place path to handle steady-state drift.

The scheduler side is also still catching up: scheduler preemption for in-place pod resize only reached alpha in Kubernetes 1.37, so a resize that needs room today waits on headroom rather than preempting lower-priority pods to make it. Until that matures, keep the recreate fallback and don't promise tenants that every resize lands instantly.

The beta caveats checklist before production

Here is the explicit gate list — resource types, runtimes, and every other precondition — verified against the 1.36 announcement and KEP-1287:

CheckStatus in 1.36What to verify on your fleet
Resource typesCPU and memory only — no hugepages, no extended or custom resourcesResize calls touching anything else are rejected
Node cgroupscgroup v2 only — required for accurate aggregate enforcementAudit node images; a mixed v1/v2 fleet resizes inconsistently
Container runtimeMust implement CRI UpdateContainerResources (containerd v2.0+ or CRI-O)Pin and verify runtime versions before enabling
OSLinux-onlyWindows node pools stay on restart-on-resize
Feature gatesPodLevelResources + InPlacePodVerticalScaling + InPlacePodLevelResourcesVerticalScaling + NodeDeclaredFeaturesAll four on; the pod-level gate is default-on in 1.36
Pod-level resizePolicyNot supported — kubelet defers to per-container policiesSet NotRequired/RestartContainer on every container, including sidecars
QoS classImmutable across a resize — validation rejects any resize that would change Guaranteed/Burstable/BestEffortSize the QoS shape at creation; resize can't fix a wrong class
resizePolicy itselfSet it at pod creation; it can't be added to a running pod laterBake it into your pod template now, not when you first resize
Memory decreasesShrinking memory in use can fail or require restart semanticsTreat down-resizes as restart-candidates; test your workloads
VPA pod-level recommendationsRoadmap, not shipped — the stated focus for the GA pushContainer-level VPA works; pod-budget recommendations don't exist yet

The runtime and cgroup rows deserve emphasis because they are fleet-wide prerequisites, not per-workload flags: a single node image still on cgroup v1 or an older containerd turns resize into a per-node lottery. And the QoS row bites PaaS defaults hardest — if your platform launches every tenant pod as BestEffort or Burstable with loose requests, no amount of in-place resizing upgrades the QoS contract; that shape is chosen once, at creation.

What a self-hosted fleet should do now

For a Cluster-API-managed fleet on owned machines, where you control the node image and the runtime version, the adoption sequence is short:

  1. Verify the node baseline. Confirm images boot cgroup v2 and ship containerd v2.0+ or CRI-O with UpdateContainerResources support. This is one image audit, not a per-tenant migration.
  2. Bake resizePolicy into the pod template. Every container your platform creates — app and sidecar alike — should carry an explicit policy from day one, since it can't be retrofitted onto running pods.
  3. Expose resize conditions to tenants. PodResizePending and PodResizeInProgress belong in your status surface next to restarts and OOMKills, so a deferred resize reads as "node full, queued" instead of a silent no-op.
  4. Keep the recreate fallback. VPA's InPlaceOrRecreate is the right model for homegrown autoscalers too: attempt the resize, watch the conditions, and recreate when the kubelet reports Infeasible or the delta is too large to apply live.
  5. Track 1.37's resize preemption alpha. Once the scheduler can preempt to make room for a resize, the "node full" failure mode shrinks from a hard fallback to a scheduling delay — that's when in-place becomes the default path rather than the attempted one.

Conclusion: right-sizing stops being a deployment

The arc from alpha in 1.27 through beta in 1.33, container-level GA in 1.35, and now pod-level beta in 1.36 points in one direction: resource sizing is becoming a control-plane write, not a rollout. For a PaaS, that collapses an entire category of operational work — the eviction orchestration, disruption budgets, and surge math that exist only because changing a number required killing a process. The beta checklist above is real but bounded, and every item on it is verifiable in an afternoon against your own node images. The platforms that adopt in-place resize early won't just restart tenants less; they'll delete code paths that only existed to apologize for restarts.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex