k8wiz
Back to Articles

Ready to test your Kubernetes knowledge? Try our Kubernetes quiz!

Kubernetes 1.35 Timbernetes: What Breaks, What's Deprecated, and How to Upgrade Without Surprises

Kubernetes 1.35 shipped on December 17, 2025 with a hard removal that stops kubelets from starting, a runtime end-of-life warning, and an API cleanup that demands action before you touch the upgrade button. This article breaks down every change that can bite you, plus the checklist to run against your cluster before you move a single node.

10 min read
By k8wiz Team
kubernetes1.35upgradecgroupkube-proxycontainerd

Kubernetes 1.35 Timbernetes: What Breaks, What's Deprecated, and How to Upgrade Without Surprises

Kubernetes 1.35, codenamed Timbernetes, landed on December 17, 2025 with 60 enhancements spread across 17 stable, 19 beta, and 22 alpha features. That's a healthy release by any measure. But the headline features aren't what will wake you up at 3 AM after an upgrade — the removals will.

This release does something Kubernetes rarely does: it turns a long-standing deprecation into a hard failure. If your nodes still run cgroup v1, the kubelet won't start. Not a warning, not a grace period — a failure mode. Pair that with the final release supporting containerd 1.x and an API version that gets pulled outright, and 1.35 is less "upgrade when convenient" and more "audit your fleet first."

Here's everything that changes, ranked by how badly it can hurt, followed by the checklist to run before you touch a single node.

The hard breaks: fix these before you upgrade

cgroup v1 support is removed, not deprecated

Kubernetes has supported cgroup v2 as stable since version 1.25. Cgroup v1 stuck around as a legacy path with well-known limitations around resource isolation and consistency. In 1.35, that legacy path is gone. The kubelet's failCgroupV1 setting now defaults to true, which means any node still running cgroup v1 will refuse to start its kubelet on upgrade.

Check every node in your fleet before you go anywhere near this upgrade:

stat -fc %T /sys/fs/cgroup
# cgroup2fs = you're fine, this node runs cgroup v2
# tmpfs     = this node will fail to boot its kubelet on 1.35

There is a failCgroupV1: false override in KubeletConfiguration that lets you bypass the check. Treat it as a last resort, not a plan. Running it means giving up memory QoS, certain swap configurations, and PSI (Pressure Stall Information) metrics — all of which depend on cgroup v2 — while you defer an OS migration that isn't going away. If your fleet includes older Linux distributions without cgroup v2 support, that's an infrastructure upgrade project, not a Kubernetes config flag.

Containerd 1.x reaches end of life — this is your last warning

Kubernetes 1.35 is the final release that supports containerd 1.7 and other 1.x LTS branches. The SIG Node community made this call as a consequence of automated cgroup driver detection work, and it's final: if you're still on containerd 1.x, you must move to containerd 2.0 or later before you upgrade past 1.35.

Kubernetes gives you a way to check exposure across the fleet without SSHing into every node — watch the kubelet_cri_losing_support metric to spot which nodes are running a containerd version that's about to fall out of support.

The StorageVersionMigration v1alpha1 API is gone

This one is easy to miss because it doesn't touch workloads directly. Kubernetes 1.35 adds a v1beta1 API for StorageVersionMigration and removes v1alpha1 outright — no deprecation window, no warning period. If you have any v1alpha1 StorageVersionMigration resources sitting in your cluster, they need to be removed and recreated against v1beta1 before you upgrade, or the upgrade will fail on those objects.

kubectl get storageversionmigrations.storagemigration.k8s.io -o jsonpath='{.items[*].apiVersion}'

If anything comes back tagged v1alpha1, deal with it first.

Deprecations with a ticking clock (not urgent, but don't ignore them)

kube-proxy's ipvs mode is now deprecated

IPVS mode gave kube-proxy a performance edge over iptables for years, but keeping it in sync with newer networking capabilities became too expensive to maintain. Kubernetes 1.35 deprecates ipvs mode — it still works, but kube-proxy now logs a warning on startup if it's configured. No removal date has been set yet, so this isn't a 1.35 emergency, but the direction is clear: nftables is the recommended mode for Linux nodes, and it's been stable since 1.33.

kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode

If that comes back ipvs, start testing nftables in a staging cluster now. For smaller clusters, plain iptables might be sufficient in the meantime.

Ingress NGINX is retiring — and it lands squarely in this upgrade window

This isn't a Kubernetes 1.35 code change, but it's impossible to separate from your 1.35 upgrade planning. In November 2025, SIG Network and the Kubernetes Security Response Committee announced that Ingress NGINX — the controller behind an estimated half of all cloud-native environments — would retire in March 2026. After that date: no releases, no bug fixes, no security patches, ever. The committees were blunt about it: staying on Ingress NGINX past retirement leaves you and your users exposed.

If you're running ingress-nginx, check your exposure now:

kubectl get pods -A -l app.kubernetes.io/name=ingress-nginx

The recommended direction is Gateway API, which addresses the annotation-sprawl and hot-reload limitations that made Ingress NGINX hard to maintain in the first place. None of the alternatives are drop-in replacements, so give this migration real project time — don't bundle it into the same maintenance window as your 1.35 upgrade.

What's new and stable — safe to adopt now

In-place Pod resize graduates to GA

Six years after it first appeared as a KEP, in-place Pod resource resizing is stable. You can now change CPU and memory requests and limits on a running Pod without recreating it — a meaningful win for stateful and batch workloads where a restart means real disruption.

apiVersion: v1
kind: Pod
metadata:
  name: resizable-worker
spec:
  containers:
    - name: worker
      image: worker:1.4
      resources:
        requests:
          cpu: "500m"
          memory: "512Mi"
        limits:
          cpu: "1"
          memory: "1Gi"
      resizePolicy:
        - resourceName: cpu
          restartPolicy: NotRequired
        - resourceName: memory
          restartPolicy: NotRequired

Resize it live with a strategic merge patch:

kubectl patch pod resizable-worker --subresource resize --patch \
  '{"spec":{"containers":[{"name":"worker","resources":{"requests":{"cpu":"1"},"limits":{"cpu":"2"}}}]}}'

Alongside it, .metadata.generation for Pods is now stable too, with a matching .status.observedGeneration. That pairing is what makes in-place resize trustworthy in automation — you can now tell, programmatically, whether the kubelet has actually applied the spec change you just made instead of guessing from Pod events.

Job API gets a stable managedBy field

The managedBy field on Jobs is GA. It lets an external controller — Kueue's MultiKueue is the primary driver here — take over Job status synchronization instead of the built-in Job controller. If you're dispatching Jobs across clusters for batch or ML training workloads, this closes a real gap: a Job created in a management cluster can now be mirrored and executed in a worker cluster with status flowing back cleanly, without the built-in controller fighting for ownership.

Traffic distribution gets a same-node option

The trafficDistribution field on Services adds PreferSameNode, which strictly prioritizes endpoints on the local node before falling back to remote ones — useful for cutting cross-node hops on latency-sensitive services. At the same time, the existing PreferClose value is renamed to PreferSameZone for clarity (the old name still works for backward compatibility, but start using the new one in new manifests).

apiVersion: v1
kind: Service
metadata:
  name: latency-sensitive-svc
spec:
  trafficDistribution: PreferSameNode
  selector:
    app: cache-reader
  ports:
    - port: 80
      targetPort: 8080

Other stable graduations worth knowing about

  • Fine-grained SupplementalGroups control — precise control over which supplemental group IDs merge with a container's primary group, closing a long-standing multi-tenancy gap.
  • Kubelet configuration drop-in directory — split kubelet config across multiple files in a directory instead of one monolithic file, which makes config management via automation far less fragile.
  • Structured Authentication Configuration — replaces the rigid --oidc-* flags with a dedicated config file defining JWT issuers, claim mappings, and validation rules, supporting multiple providers without an API server restart.

Beta features worth piloting in staging

Pod Certificates (KEP-4317) is the one to watch closely. It gives every Pod a native, short-lived cryptographic identity: the kubelet generates keys, requests a certificate through a PodCertificateRequest, and writes the credential bundle directly into the Pod's filesystem. The API server enforces node restriction at admission time. For teams currently gluing together cert-manager or SPIFFE/SPIRE for mTLS between services, this is a serious simplification — worth a staging spike even before it hits GA.

CSI service account tokens via secrets is smaller but practical: CSI drivers can now opt in to receiving service account tokens through the secrets field instead of volume context, by setting spec.serviceAccountTokenInSecrets: true on the CSIDriver object.

Alpha features to track, not deploy

  • Node declared features (KEP-5328) — nodes report which Kubernetes features they support through a new .status.declaredFeatures field, so the scheduler and admission controllers can avoid placing Pods on nodes that can't actually support what they need. This is aimed squarely at the version-skew scheduling mismatches that show up mid-rollout on large clusters.
  • Numeric toleration operators — taints and tolerations gain Gt/Lt comparison operators, so a Pod can tolerate any node with, say, an SLA-tier taint above a numeric threshold, instead of matching an exact value.
  • Mutable PersistentVolume node affinity — lets you update a PV's node affinity after creation, useful for storage migration scenarios that previously required deleting and recreating the PV.
  • Workload Aware Scheduling — early groundwork for scheduling decisions that consider a set of related Pods together rather than one at a time, which matters for distributed AI/ML training jobs where partial scheduling wastes the whole run.

Two smaller changes that catch automation off guard

kubectl now supports KYAML, a stricter YAML subset designed for Kubernetes that closes ambiguities and parsing security issues present in generic YAML. If you have CI pipelines or tooling that parses raw kubectl output and something starts behaving unexpectedly after the upgrade, check this first — you can opt out with KUBECTL_KYAML=false.

kuberc gains credential plugin allowlisting. You can now set credentialPluginPolicy and credentialPluginAllowlist in kuberc to control exactly which exec-based credential plugins a kubeconfig is allowed to invoke — a solid supply-chain hardening step if your kubeconfigs move between laptops, CI runners, and shared bastion hosts.

Your pre-upgrade checklist

Run through this in order — each step gates the next:

  1. Audit cgroup versions fleet-wide. Run stat -fc %T /sys/fs/cgroup across every node. Any tmpfs result is a node that needs an OS migration before it can run 1.35's kubelet.
  2. Confirm containerd 2.x everywhere. Check kubelet_cri_losing_support and your node images. This is the last release that tolerates containerd 1.x — there's no extension.
  3. Clear out v1alpha1 StorageVersionMigration objects. Query for them, recreate as v1beta1, and confirm none remain before you start the control plane upgrade.
  4. Check your kube-proxy mode. If it's ipvs, it still works in 1.35, but start a staged nftables migration in a non-production cluster.
  5. Inventory your Ingress NGINX footprint. This isn't a 1.35-specific change, but the retirement clock and your 1.35 upgrade window overlap — treat the migration as its own project with its own timeline.
  6. Review kubeconfig exec plugins used in CI/CD. Consider locking them down with kuberc's new credentialPluginAllowlist.
  7. Test kubectl-based automation against KYAML. If a pipeline parses raw kubectl output and starts failing, set KUBECTL_KYAML=false while you adjust the parser.
  8. Respect the version skew policy during rollout. Upgrade the control plane first, then nodes in batches — don't leave old and new kubelets mixed longer than your rollout window requires, and confirm your skew stays within Kubernetes' supported range for kubelet-to-API-server versions.

The bottom line

Kubernetes 1.35 isn't a release you can rubber-stamp through a CI pipeline and call done. The cgroup v1 removal alone can take down a node fleet if your base images haven't caught up, and the containerd EOL notice means this is the last version where procrastinating on runtime upgrades costs you nothing. Run the checklist above against a staging cluster first, confirm every node clears the cgroup and containerd checks, and only then schedule the production rollout. The features are genuinely good — in-place resize alone will save real operational pain — but they're only worth having if the upgrade that brings them doesn't take your cluster down first.

Ready to test your Kubernetes knowledge? Try our Kubernetes quiz!