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

Kubernetes 1.33: What's New and What It Means

โ€” kubernetes, k8s, cloud-native, release-notes, k8s-1.33, octarine โ€” 11 min read

Kubernetes v1.33, named "Octarine" after Terry Pratchett's Discworld, shipped on April 23, 2025 with 64 enhancements: 18 to stable, 20 to beta, 24 to alpha, and 2 deprecated or withdrawn. Most of it is not glamorous, which is a good sign for a platform this size. The changes that matter to operators are the ones that remove a workaround you have been carrying for years: resizing pods without a restart, a supported sidecar, a kube-proxy backend that scales, and an API server that stops falling over on large List requests.

I have grouped the release the way I would brief a team: what graduated and is safe to lean on, what is in beta and worth piloting, what is alpha and worth watching, and what got removed and needs action before you upgrade. For anything you turn on, I call out the gate, the default, and the caveat, because "beta" does not always mean "on."


What graduated to stable

These are GA in 1.33. You can build on them without a feature gate.

Sidecar containers

The sidecar has been a pattern for years, implemented by hand with a plain container and a lot of lifecycle glue. In 1.33 it is a first-class, stable feature (KEP-753). You declare a sidecar as an init container with restartPolicy: Always.

spec:
initContainers:
- name: log-shipper
image: fluent/fluent-bit
restartPolicy: Always # this makes it a sidecar

The runtime guarantees the ordering you always wanted: sidecars start before the app containers, stay running for the whole pod lifecycle, and terminate after the main containers exit. Sidecars also get startup, readiness, and liveness probes, and their out-of-memory score is aligned with the primary containers so the kernel does not kill your mesh proxy first under memory pressure. If you run Istio, Linkerd, or any logging or metrics sidecar, this is the model to move to.

In-place pod resize (beta, but the headline change)

Before 1.33, changing a container's CPU or memory request meant replacing the pod. For a stateful workload that is a restart you did not want. In-place resize (KEP-1287), alpha since 1.27, is beta and on by default in 1.33. You change resources through a dedicated resize subresource, and the kubelet actuates the change without recreating the pod.

kubectl patch pod mypod --subresource resize --patch \
'{"spec":{"containers":[{"name":"app","resources":{"requests":{"cpu":"1","memory":"1Gi"}}}]}}'
kubectl patch --subresource resize
โ”‚
โ–ผ
apiserver validates โ”€โ”€โ–ถ pod.spec.containers[].resources updated
โ”‚
โ–ผ
kubelet actuates on the node (no pod recreation)
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ–ผ โ–ผ
PodResizePending PodResizeInProgress โ† new pod conditions to watch

Two caveats a staff engineer cares about. First, memory limit decreases are the hard case, since you cannot always reclaim memory from a running process, so treat downward memory resizes carefully. Second, watch the new PodResizePending and PodResizeInProgress conditions rather than assuming the patch took effect immediately. Use a kubectl new enough to understand the resize subresource; subresource support in kubectl (--subresource) is itself GA in 1.33.

Batch: Job success policy and per-index backoff

Two Jobs features went stable together, and they change how you run large indexed workloads. Job success policy (KEP-3998) lets you declare the Job done when enough indexes finish, instead of requiring all of them. Backoff limit per index (KEP-3850) gives each index its own retry budget, so one poison index does not burn the whole Job's backoffLimit and fail everything.

apiVersion: batch/v1
kind: Job
metadata:
name: index-job
spec:
completionMode: Indexed
parallelism: 10
completions: 10
successPolicy:
rules:
- succeededCount: 5 # done once any 5 indexes succeed
backoffLimitPerIndex: 2 # retry each index up to twice
maxFailedIndexes: 1 # give up if more than 1 index fails outright

This maps directly onto leader-worker and simulation workloads, where partial completion is a real success condition and wasted retries cost real money.

Networking: nftables kube-proxy and topology-aware routing

The nftables backend for kube-proxy is stable (KEP-3866). On clusters with many Services, the old iptables backend spends significant time reprogramming linear rule chains; nftables scales much better. Note the default did not change: iptables is still the default on Linux for compatibility, so nftables is an opt-in you migrate to, following the official migration guide, ideally after testing.

Topology-aware routing also went GA, with a trafficDistribution: PreferClose field on the Service (KEP-2433, KEP-4444). It tells kube-proxy to prefer endpoints in the same zone, which cuts cross-zone latency and inter-zone data transfer cost in multi-zone clusters.

apiVersion: v1
kind: Service
spec:
trafficDistribution: PreferClose

Multiple Service CIDRs (KEP-1880) is GA too, built on two now-stable API objects, ServiceCIDR and IPAddress. You can extend the pool of ClusterIP addresses by creating a new ServiceCIDR, instead of being stuck with the single range you picked at cluster creation.

Storage and security graduations

Volume populators (KEP-1495) are GA: a PVC can be pre-populated from a custom data source through dataSourceRef, validated by the volume-data-source-validator controller and the VolumePopulator CRD. Always honor PersistentVolume reclaim policy (KEP-2644) is GA and fixes a real leak: if you deleted a PV before its PVC, a Delete reclaim policy could be skipped and leave the backing disk behind. Kubernetes now sets finalizers so the policy runs regardless of deletion order.

On the security side, bound ServiceAccount token improvements (KEP-4193) are GA. Tokens now carry a unique identifier (JWT jti) and node information, and support node-specific restrictions, so a leaked token is easier to trace and can be scoped to the node it belongs to. This is the kind of change you get for free by upgrading.

Also worth knowing, since they land silently: CPU Manager can now reject workloads that are not SMT-aligned (KEP-2625), matchLabelKeys and mismatchLabelKeys are stable in pod affinity (KEP-3633), and recursive read-only mounts (KEP-3857) are GA.


Worth piloting: notable beta features

Streaming List responses

This is the reliability fix I would highlight to anyone running large clusters. The API server used to encode a full List response into one contiguous buffer and hold it until the whole thing was transmitted. A List that returns hundreds of megabytes, or several of them at once during network congestion, could pin gigabytes of memory and OOM the kube-apiserver. Because encoding/json reuses buffers from a sync.Pool, one large response also left oversized buffers reserved, keeping memory high even after it completed.

Before: encode whole list โ”€โ–ถ [ one big buffer: 100s of MB ] โ”€โ–ถ write
(held until fully sent, may OOM apiserver)
After: encode item by item โ”€โ–ถ item โ”€โ–ถ write โ”€โ–ถ free โ”€โ–ถ item โ”€โ–ถ write โ”€โ–ถ free
(memory stays flat regardless of list size)

The streaming encoder processes the Items array one object at a time and frees memory as each item is sent. It validates struct tags first to guarantee byte-for-byte identical output, so clients need no changes. In the project's benchmark, 10 concurrent List requests each returning 1 GB dropped API server memory from roughly 70 to 80 GB down to about 3 GB, a 20x reduction.

In-place resize, image volumes, and user namespaces

In-place resize is beta and covered above. Two more beta features need a note on defaults:

Image volumes (KEP-4639) let you mount an OCI image or artifact as a read-only volume, with subPath to mount a specific directory. It is beta in 1.33 but off by default, so you must enable the feature gate on both the kube-apiserver and the kubelet before using it.

volumes:
- name: model
image:
reference: quay.io/org/model-artifact:v2
pullPolicy: IfNotPresent
volumeMounts:
- name: model
mountPath: /data
subPath: weights

User namespaces (KEP-127), one of the oldest KEPs in the project, moved to on-by-default beta. Nothing changes for existing pods unless you opt in per pod with hostUsers: false, which maps container UIDs and GIDs to unprivileged host ranges and blunts a class of container-escape vulnerabilities. Alongside it, supplementalGroupsPolicy: Strict (KEP-3619) is beta and on by default (when the gate is enabled), which stops a pod inheriting implicit groups from the container image's /etc/group, and the procMount option (KEP-4265) is on-by-default beta for finer control over /proc masking, useful when running nested unprivileged containers.

Dynamic Resource Allocation and scheduling

DRA, the API for requesting and sharing devices like GPUs and NICs, keeps maturing. Structured parameters (KEP-4381) stay beta with a simpler resource.k8s.io/v1beta2 API and rolling-update support for driver DaemonSets, and DRA for network interfaces (KEP-4817) graduated to beta. The scheduler also got asynchronous preemption (KEP-4832), which moves the expensive pod-deletion API calls off the hot path so preemption does not stall other scheduling, a real win on high-churn clusters.

On the security side, ClusterTrustBundles (KEP-3257) reached beta. It is a cluster-scoped resource for holding X.509 trust anchors (root certificates), giving in-cluster signers a standard way to publish and distribute trust to workloads instead of every component inventing its own CA-distribution mechanism.


Worth watching: notable alpha features

Alpha means off by default and not yet safe for production, but these signal where things are heading:

  • Custom container stop signals (KEP-4960): set lifecycle.stopSignal in the pod spec instead of baking it into the image. Requires spec.os.name to be set.
  • Robust image pull authentication (KEP-2535): the kubelet re-checks pull credentials even when the image is already cached, closing a gap where a cached image bypassed authorization.
  • Storage capacity scoring (KEP-4049): the scheduler's VolumeBinding plugin can score nodes by free storage, so you can prefer the most or least full node for dynamic provisioning.
  • Mutable CSI node allocatable count: CSI drivers can update a node's reported volume capacity at runtime, reducing scheduling failures from stale limits.
  • Configurable HPA tolerance (KEP-4951): set a per-HPA tolerance so the autoscaler stops reacting to small metric noise.
  • Tune CrashLoopBackOff (KEP-4603): kubelet-level knobs to configure the restart backoff instead of the fixed exponential curve.
  • .kuberc for kubectl (KEP-3104): keep aliases and defaults, like always using server-side apply, separate from cluster credentials in kubeconfig. Enable with KUBECTL_KUBERC=true.

Also in this release

This post covers the operator-facing changes in depth. For completeness, here are the remaining 1.33 enhancements I did not expand on, with links to their KEPs.

Stable: CRD validation ratcheting and Portworx in-tree to CSI migration.

Beta: CPU Manager distribute CPUs across NUMA nodes, pop pod from backoffQ when activeQ is empty, and declarative validation with validation-gen.

Alpha: pod generation and observedGeneration, node topology labels via the downward API, PSI metrics on cgroup v2, split L3 cache awareness in CPU Manager, projected ServiceAccount tokens for kubelet image credential providers, and the four DRA alphas: device taints and tolerations, prioritized device lists, admin access to ResourceClaims, and partitionable devices.


Deprecations and removals: act before you upgrade

This is the section to read carefully, because two items are removals, not deprecations.

The in-tree gitRepo volume driver was removed in 1.33 (KEP-5040). It was deprecated back in 1.11 and had a remote-code-execution risk. The gitRepo field still exists in the API, so pods are admitted, but a kubelet with the GitRepoVolumeDriver gate off will refuse to run them. You can re-enable the gate for a few releases as a migration bridge; the plan is to remove it entirely in 1.39. Move to git-sync or an init container that clones into an emptyDir.

The .status.nodeInfo.kubeProxyVersion field was removed (KEP-4004). It was set by the kubelet, was often inaccurate, and has been disabled by default since 1.31. If any tooling reads it, fix that before upgrading.

The Endpoints API is deprecated (KEP-4974) in favor of EndpointSlice, which has been stable since 1.21 and handles large endpoint counts and dual-stack. This only affects code that reads the Endpoints API directly; migrate those consumers to EndpointSlice. Nothing is removed yet.

Host network support for Windows pods (the alpha KEP-3503) was withdrawn and removed in 1.33 due to containerd behavior. This does not affect HostProcess containers, which remain the supported path for host-level access on Windows.


Upgrade notes

  1. Audit for removals first: any reader of .status.nodeInfo.kubeProxyVersion, any pod using gitRepo volumes, and any direct consumer of the Endpoints API. These break or degrade on upgrade.
  2. Pilot in-place resize on non-critical stateful workloads and wire PodResizePending and PodResizeInProgress into your dashboards before trusting it.
  3. Move sidecars to the native init-container-with-restartPolicy: Always model and drop the lifecycle hacks.
  4. Evaluate the nftables kube-proxy backend on a test cluster if your Service count is large, but remember iptables is still the default, so this is a deliberate migration.
  5. Turn on trafficDistribution: PreferClose for multi-zone Services to cut cross-zone cost and latency.
  6. If you run large clusters, upgrade for streaming List responses alone; the apiserver memory behavior is materially better.
  7. Treat image volumes and user namespaces as opt-in: image volumes need the gate on apiserver and kubelet, and user namespaces need hostUsers: false per pod.

Closing thoughts

Kubernetes 1.33 is a maintenance-forward release. The stable graduations remove workarounds (sidecars, resize, per-index Job backoff, nftables), and the beta work targets the failure modes that actually page you (apiserver memory on large Lists, scheduler stalls under churn). The two removals, gitRepo and the kubeProxyVersion field, are the only items that demand action before you roll forward. Read the release notes for the full list, but if you upgrade for two reasons, make them streaming List responses and in-place pod resize.

Further reading

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