Skip to content
Yuvraj ๐Ÿงข
Github - yindiaGithub - tqindiaContact

Kubernetes 1.35: In-Place Pod Resize Is GA, and cgroup v1 Nodes Stop Booting

โ€” kubernetes, k8s-1.35, release-notes, in-place-resize, cgroup, cloud-native โ€” 11 min read

The question that matters for Kubernetes 1.35 is simple: should you take the upgrade now? The case for yes is one clearly useful feature. In-place Pod resize is finally GA, so you can change a Pod's CPU and memory without restarting it. The case for caution is one clearly disruptive change. Nodes running the legacy cgroup v1 will refuse to start the kubelet by default. This post weighs both, plus the deprecations that turn 1.35 into a hard deadline for containerd 1.x and Ingress-NGINX, and ends with a verdict.

Previous release: Kubernetes 1.34. Next: 1.36 (coming soon).

Here is what you are deciding on, at a glance:

  • In-place Pod resize is GA (KEP #1287). You can resize CPU and memory on a running Pod. Only CPU and memory are allowed.
  • cgroup v1 is effectively cut off. From 1.35 the kubelet sets failCgroupV1: true by default, so a node on cgroup v1 will not start the kubelet. Migrate nodes to cgroup v2, or set failCgroupV1: false as a temporary escape hatch.
  • 1.35 is the last release to support containerd 1.x. Move to containerd 2.0+ before you upgrade to 1.36.
  • Image volumes are on by default (KEP #4639), but need containerd 2.1 or later.
  • Deprecations to note: ipvs mode in kube-proxy (migrate to nftables) and the Ingress-NGINX controller (best-effort until March 2026, then archived; move to Gateway API).
  • Upgrade warning: the kubelet flag --pod-infra-container-image was removed, and the StorageVersionMigration and VolumeAttributesClass v1alpha1 APIs were removed.

In-place Pod resize is the reason to upgrade

Start with what earns the upgrade: workload right-sizing. For years, changing a Pod's resources meant deleting and recreating it, which is disruptive for stateful and long-running jobs. In-place resize removes that constraint and reaches GA in 1.35.

Before this, changing a container's CPU or memory request meant the scheduler replaced the Pod. In-place resize, GA in 1.35, lets the kubelet apply the new resources to the running Pod through a dedicated resize subresource (KEP #1287, changelog).

The lifecycle, from request to applied, is a small state machine.

[ Pod running ]
โ”‚ kubectl patch --subresource resize
โ–ผ
[ PodResizePending ] โ”€โ”€โ–ถ [ PodResizeInProgress ] โ”€โ”€โ–ถ [ Pod running, new resources ]
(validated) (kubelet actuates) (no restart)

Caption: in-place resize moves a Pod through pending and in-progress states without recreation.

Patch the resize subresource to change resources on a live Pod:

kubectl patch pod mypod --subresource resize --patch \
'{"spec":{"containers":[{"name":"app","resources":{"requests":{"cpu":"1","memory":"1Gi"}}}]}}'

Notice two limits from the changelog: only CPU and memory can be resized, and the change is tracked in new PodStatus fields (Resources, AllocatedResources). Memory decreases are now supported, which was the hard case.

Adopt now for stateful workloads and long-running jobs that need to scale without downtime. Watch the PodResizePending and PodResizeInProgress conditions rather than assuming the patch applied instantly.

Why resize took eight years to land

In-place resize took eight years to reach GA (alpha in 1.27) because the hard parts are not the API. Shrinking a memory limit on a running process is not always safe, since you cannot force a process to give memory back. The GA design handles this by tracking desired versus allocated resources in PodStatus and letting the kubelet actuate when it can, rather than guaranteeing an instant change. That is why you watch PodResizeInProgress instead of assuming the patch took effect. The scope was also deliberately kept to CPU and memory; extending it to other resources like GPUs is future work (KEP #1287).

The upgrades you will quietly appreciate

The rest of 1.35 is less dramatic but worth knowing before you commit.

Image volumes are on by default Beta

Image volumes let a Pod mount an OCI (Open Container Initiative) artifact or image as a read-only volume, so you can ship configs, binaries, or ML models separately from the app image and skip init containers (KEP #4639). Beta since 1.33, they are enabled by default in 1.35.

volumes:
- name: model
image:
reference: registry.example.com/models/llama:v1
pullPolicy: IfNotPresent
volumeMounts:
- name: model
mountPath: /models

Notice the requirement: this needs containerd 2.1 or later. On older runtimes the volume will not mount.

Pod certificates for workload identity Beta

Workload identity inside a cluster has leaned on ServiceAccount tokens, which are awkward for mutual TLS. In 1.35 the kubelet can generate a key, request a certificate through a PodCertificateRequest, and write the bundle into the Pod's filesystem, with automated rotation (KEP #4317). Beta.

Adopt if you need certificate-based, per-Pod identity for mTLS between services.

PreferSameNode traffic distribution is GA GA

This completes the story 1.34 started. The Service trafficDistribution field gains PreferSameNode (prefer an endpoint on the same node, fall back to remote) at GA, and the old PreferClose value is now the alias PreferSameZone (KEP #3015).

apiVersion: v1
kind: Service
spec:
trafficDistribution: PreferSameNode

Notice PreferSameNode cuts a network hop for node-local traffic; use PreferSameZone when you only need to stay in-zone.

Reliable Pod and Deployment status tracking GA Beta

Two observability fixes. Pods now increment .metadata.generation on every spec change and report .status.observedGeneration, so controllers can tell whether the kubelet has processed the latest spec (KEP #5067, GA). Deployments gain a beta .status.terminatingReplicas count, so you can distinguish a stable Deployment from one still cleaning up Pods.

Adopt: nothing required. These make automation and dashboards more accurate.

Job managed-by mechanism is GA GA

The Job .spec.managedBy field lets an external controller own a Job's status sync instead of the built-in controller (KEP #4368). This is the foundation for MultiKueue, which schedules Jobs across clusters. Adopt if you run multi-cluster batch with Kueue.

KYAML is on by default Beta

KYAML, the safer YAML subset introduced as alpha in 1.34, is beta and enabled by default in 1.35 (KEP #5295). kubectl get -o kyaml works out of the box; set KUBECTL_KYAML=false to disable. Every KYAML file is valid YAML, so nothing downstream breaks.

Batch and AI: gang scheduling and user namespaces Alpha Beta

Two items for AI and batch. Gang scheduling arrives as alpha through a Workload API and PodGroup, giving all-or-nothing scheduling so a distributed job either gets all its Pods or none (KEP #4671). User namespaces continue in beta, mapping container user and group IDs to unprivileged host ranges (KEP #127). Adopt gang scheduling only on test clusters for now; it is alpha.

Smoother kubelet restarts Behaviour change

A quieter but welcome fix: restarting the kubelet no longer flaps healthy Pods to NotReady. The kubelet now restores container state from the runtime on startup, so traffic keeps flowing during node maintenance (release blog). Nothing to configure.

What will break when you upgrade

Now the other side of the decision: what 1.35 takes away. The theme is retiring old foundations. cgroup v1, the first Linux control-group interface, has been superseded by cgroup v2 since 1.25. Supporting both was a maintenance drag and a source of inconsistent resource behaviour. containerd 1.x and the ipvs kube-proxy backend are in the same category. 1.35 draws a line under all three so the project can focus on the modern replacements. Read this section before upgrading.

cgroup v1 nodes will not start the kubelet Behaviour change

This is the change most likely to bite you, so it appears here and in the deprecations section. From 1.35 the kubelet defaults failCgroupV1 to true, meaning it refuses to start on a node using cgroup v1 (changelog, KEP #5573).

kubelet starts on a node
โ”‚
cgroup v2?
โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”
yes no
โ”‚ โ”‚
start failCgroupV1=true (default) โ”€โ”€โ–ถ kubelet refuses to start
โ”‚
failCgroupV1=false โ”€โ”€โ–ถ starts, with cgroup v1 (temporary)

Caption: on 1.35 a cgroup v1 node needs an explicit override to run the kubelet.

The fix is to migrate nodes to cgroup v2, available since 1.25. If you need a short reprieve, set failCgroupV1: false in the kubelet configuration (for kubeadm, the kube-system/kubelet-config ConfigMap). Treat that as temporary; cgroup v1 is on its way out.

Adopt: check every node's cgroup version before upgrading. This is a boot-time failure, not a warning.

The removals and deadlines are wider than cgroup v1. 1.35 removes several APIs and flags and sets three deadlines.

Removed in 1.35

ItemReplacementActionSource
kubelet flag --pod-infra-container-imageRuntime-managed sandbox imageRemove the flag from kubelet args before upgrade or the kubelet fails to startchangelog
StorageVersionMigration v1alpha1 APIv1beta1Delete v1alpha1 resources before upgradingchangelog
VolumeAttributesClass in storage.k8s.io/v1alpha1storage.k8s.io/v1 (GA in 1.34)Move to the v1 APIchangelog
Feature gates SizeMemoryBackedVolumes, ComponentSLIs, UserNamespacesPodSecurityStandards, StrictCostEnforcementForVAP/Webhooks (all GA)Behaviour is permanentRemove any gate overrideschangelog
Env var KUBECTL_OPENAPIV3_PATCHNoneRemove if setchangelog

Deprecated in 1.35 (removed later)

Note on containerd: the 1.35 release blog stated the removal would land in 1.36. It has since slipped twice (1.36 deferred it to 1.37, and 1.37 deferred it to 1.38), aligned with containerd 1.7 end of support. Do the containerd 2.0 migration now, but verify the actual removal in the target release's changelog rather than trusting the announced version.

ItemTarget removalReplacementActionSource
cgroup v1 (kubelet default failCgroupV1: true)Being retiredcgroup v2Migrate nodes to cgroup v2KEP #5573
containerd 1.x supportRemoved in 1.37 (see note)containerd 2.0+Upgrade the runtime; watch kubelet_cri_losing_supportrelease blog
kube-proxy ipvs modeFuture releasenftables modeMigrate the kube-proxy backendKEP #5495
Ingress-NGINX controllerArchived after March 2026Gateway APIPlan migration to Gateway APIIngress-NGINX retirement

To find nodes still on cgroup v1 before you upgrade, check the node's cgroup mount:

kubectl get nodes -o name | while read n; do
echo "$n"; kubectl debug "$n" -it --image=busybox -- \
sh -c 'stat -fc %T /sys/fs/cgroup' 2>/dev/null
done

Notice cgroup2fs means cgroup v2 (fine); tmpfs means cgroup v1 (must migrate). Adjust for your node-access method.

To find Services still using the old traffic value:

kubectl get svc -A -o json \
| jq -r '.items[] | select(.spec.trafficDistribution=="PreferClose") | "\(.metadata.namespace)/\(.metadata.name)"'

Notice these should move to PreferSameZone.

The changes that fail closed

From the changelog's Urgent Upgrade Notes and deprecation section:

  • cgroup v1 kubelet refuses to start. failCgroupV1 defaults to true. A cgroup v1 node will not run the 1.35 kubelet without failCgroupV1: false. kubeadm's SystemVerification preflight also errors on cgroup v1 with a 1.35 kubelet.
  • --pod-infra-container-image removed. Non-kubeadm clusters must strip this flag from kubelet config before upgrading, or the kubelet will not start. kubeadm clusters must remove it from extraArgs.
  • v1alpha1 API removals. StorageVersionMigration and VolumeAttributesClass v1alpha1 are gone. Delete any stored v1alpha1 objects first.
  • DynamicResourceAllocation gate is locked on. GA in 1.34, it can no longer be disabled.
  • KYAML and image volumes default on. KYAML output is default (disable with KUBECTL_KYAML=false); image volumes need containerd 2.1+.
  • In-place resize is CPU and memory only. Attempts to resize other resource types are rejected.

Version skew rules are unchanged: upgrade the control plane before nodes, and keep the kubelet within the supported skew of the API server (version skew policy).

Sequencing the upgrade

With the trade-offs clear, here is a safe order.

1. inventory node cgroup versions โ”€โ”€โ–ถ migrate cgroup v1 nodes to v2
2. remove --pod-infra-container-image from kubelet config
3. delete v1alpha1 StorageVersionMigration / VolumeAttributesClass objects
4. upgrade container runtime to containerd 2.0+ (last chance before 1.36)
5. upgrade control plane
6. upgrade nodes (kubelet, CRI); confirm kubelet starts (cgroup v2)
7. keep 1.34 control-plane images for rollback

Caption: recommended 1.34 to 1.35 upgrade order.

Specifics:

  1. cgroup version is the first thing to check. A cgroup v1 node will hard-fail on the new kubelet.
  2. Remove --pod-infra-container-image everywhere it appears.
  3. Scan for v1alpha1 StorageVersionMigration and VolumeAttributesClass objects and remove them.
  4. containerd 2.0+ is required before 1.36, so 1.35 is the natural time to do it.
  5. Rollback: keep 1.34 control-plane images. In-place resize is GA and backward compatible; a rollback does not strand resized Pods.

Try these on a test cluster

  1. Resize a running Pod: kubectl patch pod <p> --subresource resize --patch '{"spec":{"containers":[{"name":"app","resources":{"requests":{"cpu":"500m"}}}]}}' and watch the conditions.
  2. Check a node's cgroup version: stat -fc %T /sys/fs/cgroup (expect cgroup2fs).
  3. Mount an image volume (on containerd 2.1+) and read a file from it.
  4. Switch a test Service to trafficDistribution: PreferSameNode.
  5. Confirm KYAML output: kubectl get deploy -o kyaml.

The call

So, is 1.35 worth taking now? Yes, provided you do the node work first. Kubernetes 1.35 gives you in-place Pod resize at GA and takes away cgroup v1. The must-do list is short and hard-edged: migrate cgroup v1 nodes, drop the removed kubelet flag, clear the v1alpha1 APIs, and move to containerd 2.0+ before 1.36. Start the Ingress-NGINX to Gateway API migration now, before the March 2026 archive date.

Next in this series: Kubernetes 1.36.

References

Release and dates:

Features:

Deprecations:

ยฉ 2026 by Yuvraj ๐Ÿงข. All rights reserved.
Theme by LekoArts