What's New in Kubernetes 1.33–1.37: Native Sidecars, In-Place Resize, preStop Sleep, Gateway API and Life After ingress-nginx
The changes in Kubernetes 1.33–1.37 that matter most to app developers are native sidecars (1.33), preStop sleep (1.34), in-place resize (1.35), user namespaces (1.36) and HPA tolerance (1.37). With real output we see a sidecar ignoring SIGTERM stretch a Job to 39 seconds, the externalIPs and Endpoints deprecations, KYAML, the ingress-nginx retirement and Gateway API v1.7.
Version note: The examples in this post were run with kind 0.33 and Kubernetes 1.37, kubectl 1.37 and .NET 10. Version information comes from the kubernetes.io release posts and Gateway API's (API: Application Programming Interface) GitHub release notes, as of November 2026.
Guess First
You add a small log shipper to a Job as a native sidecar: under initContainers, with restartPolicy: Always. The main container finishes its work in 6 seconds. The promise of native sidecars is that the Job no longer hangs forever because of the sidecar. How many seconds does the Job take: 6, 7, or longer? A hint: the sidecar's command is sh -c "tail -f ... | sed ...". The result of the first attempt shows how a new Kubernetes feature meets an old Linux reality.
Kubernetes' Release Rhythm and the Scope of This Post
Kubernetes ships three minor versions a year: 1.33 (April 2025), 1.34 (August 2025), 1.35 (December 2025), 1.36 (April 2026) and 1.37 (August 2026). In each release dozens of features move to alpha, beta or GA (General Availability); most of them concern cluster administrators. This post collects only those that affect a developer who writes a .NET app and owns its manifests, and shows in which post of the series each one was used. Don't guess whether a feature is on in your cluster, ask the API server; on our 1.37 cluster on kind:
$ kubectl get --raw /metrics | grep '^kubernetes_feature_enabled' | grep -E '...'
kubernetes_feature_enabled{name="ContainerRestartRules",stage="BETA"} 1
kubernetes_feature_enabled{name="HPAConfigurableTolerance",stage=""} 1
kubernetes_feature_enabled{name="HPAScaleToZero",stage="BETA"} 1
kubernetes_feature_enabled{name="ImageVolume",stage=""} 1
kubernetes_feature_enabled{name="InPlacePodVerticalScaling",stage=""} 1
kubernetes_feature_enabled{name="PodLevelResources",stage="BETA"} 1
kubernetes_feature_enabled{name="UserNamespacesSupport",stage=""} 1
An empty stage means the feature is GA and its feature gate remains only for backward compatibility. On managed clusters alpha features are usually off; whether beta ones are on depends on the provider.
| Feature | GA in | Where in the series |
|---|---|---|
| Native sidecar containers | 1.33 | 03 and this post |
Job: backoffLimitPerIndex, success policy | 1.33 | addition to 13 |
preStop sleep action (including 0 seconds) | 1.34 | 10 and this post |
Job: podReplacementPolicy | 1.34 | addition to 13 |
| In-place pod resize | 1.35 | 11, 16 |
User namespaces (hostUsers: false) | 1.36 | 15 |
| Image volumes (an OCI image as a volume) | 1.36 | this post |
| Per-HPA (Horizontal Pod Autoscaler) tolerance | 1.37 | 11 |
kubectl get -o kyaml | 1.37 | this post |
Native Sidecars: Side Containers That Actually Finish in Jobs
We covered native sidecars, GA in 1.33, within the pod lifecycle in the third post of the series: a container in initContainers with restartPolicy: Always starts before the main containers, runs alongside them, and shuts down after them. In the past sidecars were ordinary containers, so a Job's pod whose work was done never finished because of the sidecar, and everyone wrote patches like "send the sidecar a 'shut down' signal through a file". The Job from Guess First:
apiVersion: batch/v1
kind: Job
metadata:
name: export-orders
namespace: whatsnew
spec:
backoffLimit: 0
template:
spec:
restartPolicy: Never
initContainers:
- name: log-shipper
image: busybox:1.37
restartPolicy: Always
command: ["sh", "-c", "touch /logs/export.log; tail -n +1 -f /logs/export.log | sed 's/^/[shipped] /'"]
volumeMounts:
- name: logs
mountPath: /logs
containers:
- name: export
image: busybox:1.37
command: ["sh", "-c", "for i in 1 2 3; do echo \"exported batch $i\" >> /logs/export.log; sleep 2; done"]
volumeMounts:
- name: logs
mountPath: /logs
volumes:
- name: logs
emptyDir: {}
NAME STATUS COMPLETIONS DURATION AGE
export-orders Complete 1/1 39s 39s
log-shipper {"terminated":{...,"exitCode":137,"finishedAt":"2026-11-08T05:07:13Z","reason":"Error","startedAt":"2026-11-08T05:06:36Z"}}
export {"terminated":{...,"exitCode":0,"finishedAt":"2026-11-08T05:06:42Z","reason":"Completed","startedAt":"2026-11-08T05:06:36Z"}}
The Job finished, so the native sidecar did its job; but a 6-second task took 39 seconds. The main container finished at 05:06:42, Kubernetes sent SIGTERM (the Unix termination signal) to the sidecar, the sidecar didn't react, and at the end of the 30-second grace period it was killed with SIGKILL (the kill signal that can't be caught), exit code 137. The reason is the same as what we saw with the .NET process in the sixteenth post: sh, being PID (Process ID) 1 in the container, doesn't receive signals it has no handler for. On top of that, the lines buffered in sed went with the SIGKILL, so the shipper's log stayed empty; the sidecar we added to ship logs lost the logs. The version that catches the signal:
command: ["sh", "-c", "trap 'exit 0' TERM; touch /logs/export.log; tail -n +1 -f /logs/export.log & wait"]
NAME STATUS COMPLETIONS DURATION AGE
export-orders-v2 Complete 1/1 10s 10s
exported batch 1
exported batch 2
exported batch 3
log-shipper 0 2026-11-08T05:07:28Z 2026-11-08T05:07:35Z
export 0 2026-11-08T05:07:28Z 2026-11-08T05:07:34Z
The sidecar shut down one second after the main container with exit code 0, and the whole log is there. Native sidecars solve the ordering on the Kubernetes side; making the sidecar respond properly to SIGTERM is still your job. Sidecars' resources can also be changed with the in-place resize from the eleventh post (since 1.33; regular init containers and ephemeral containers excluded). ContainerRestartRules, beta in 1.35, brings per-container restart policies and rules based on exit codes; once it's GA we'll be able to write rules in Jobs like "restart the container on exit code 42 but don't fail the pod".
preStop sleep: Graceful Shutdown on a Shell-less Image
The problem from the tenth post: when a pod is deleted, removal from the endpoints and SIGTERM happen at the same time, and requests arriving in between get 502s. The old fix was preStop: exec: command: ["sleep", "5"], but there's no sleep binary in a chiseled image. The sleep action, GA in 1.34, has the kubelet do it itself:
apiVersion: v1
kind: Pod
metadata:
name: api-prestop
namespace: whatsnew
spec:
terminationGracePeriodSeconds: 30
containers:
- name: api
image: bookstore-api:1.2.0
lifecycle:
preStop:
sleep:
seconds: 5
delete issued at 05:07:49
pod "api-prestop" deleted from whatsnew namespace
05:07:54.510 info: Bookstore.Api[0] ApplicationStopping: SIGTERM received, draining
05:07:54.520 info: Bookstore.Api[0] ApplicationStopped
Five seconds after the delete command the .NET app received SIGTERM; in those five seconds kube-proxy and the Gateway had time to remove the pod from the endpoint list. The sleep is deducted from terminationGracePeriodSeconds: in this example the app had 25 seconds left to shut down, and HostOptions.ShutdownTimeout must stay below that. Since 1.34, seconds: 0 is valid too; it can be used to "turn off" a preStop in a chart.
In-Place Resize, User Namespaces and HPA Tolerance
We saw three GAs with real output in earlier posts of the series; here we only collect their outcomes. In-place pod resize (1.35) grew a pod without a restart in the eleventh post, but .NET's GC (Garbage Collector) heap limit and Environment.ProcessorCount didn't change; for memory you need GC.RefreshMemoryLimit() or resizePolicy: RestartContainer. In the sixteenth post the same feature let us open memory headroom in a pod to take a dump. On the VPA (Vertical Pod Autoscaler) side, the InPlaceOrRecreate mode went GA in VPA 1.6. User namespaces (1.36) mapped UID (User ID) 0 in the container to 1017053184 on the node in the fifteenth post. Per-HPA tolerance (1.37) lets you write your own threshold instead of the global 10% for large Deployments. Also in 1.37, the HPA's own scale-to-zero support became beta and enabled by default; it works with Object and External metrics, and for the queue scenario KEDA (Kubernetes Event-driven Autoscaling) is still the shortest path.
Deprecations: externalIPs and Endpoints
Two deprecations affect application developers directly. The first, in 1.33, is the v1 Endpoints API (Application Programming Interface): EndpointSlice replaces it, and in the seventeenth post we saw kubectl get endpoints's warning and how it shows up in the API server metric. If your own code reads Endpoints (e.g. service discovery with KubernetesClient), move it to EndpointSlice. The second, in 1.36, is Service.spec.externalIPs:
$ kubectl apply -f svc-externalip.yaml
Warning: spec.externalIPs is deprecated and may no longer be implemented in some clusters
service/api-external created
externalIPs carried a security problem: anyone with access to the cluster could pull traffic destined for any IP (Internet Protocol) address into their own Service. According to the Kubernetes project's plan, kube-proxy support will be turned off by default in 1.40 at the earliest, and the field will be removed completely in 1.43 at the earliest. The right way to expose services externally is a LoadBalancer Service or Gateway API.
KYAML and Image Volumes
KYAML (a Kubernetes-defined subset of YAML Ain't Markup Language), GA in 1.37, works like this: strings are always quoted, structures use braces, and there's no indentation-dependent ambiguity. It prevents classic YAML surprises like Norway's country code NO turning into false, and the output is still valid YAML:
$ kubectl -n whatsnew get svc api-external -o kyaml
---
{
apiVersion: "v1",
kind: "Service",
metadata: {
...
name: "api-external",
namespace: "whatsnew",
},
spec: {
clusterIP: "10.96.136.147",
...
externalIPs: [
"192.0.2.10",
],
You don't need to convert your manifests to KYAML; but it's useful for checking whether the YAML a chart produces has the value types you expect. Image volumes (GA in 1.36) mount an OCI (Open Container Initiative) image as a read-only volume: a way to separate a large machine learning model or a static data set from the application image and ship it with its own version. Nodes need containerd 2.1 or later.
After ingress-nginx, and Gateway API v1.7
The change we discussed in the sixth post is now complete: the ingress-nginx project was retired on 24 March 2026, its GitHub repository is read-only, and its last release is controller-v1.15.1. Existing installations keep working, but there are no more security patches. The Ingress API is GA and there are no plans to remove it, but it is frozen; new features only go into Gateway API. The options fall roughly into three groups: open source Gateway API implementations such as Envoy Gateway, NGINX Gateway Fabric, Traefik and Istio (we used Envoy Gateway in the series); providers' managed Gateways (the GKE (Google Kubernetes Engine) Gateway controller, Application Gateway for Containers and the app routing add-on's Gateway API mode on AKS (Azure Kubernetes Service), the AWS (Amazon Web Services) Load Balancer Controller on EKS (Amazon Elastic Kubernetes Service)); and, for the transition, moving to another controller that supports Ingress. To convert existing Ingress resources, ingress2gateway 1.0 came out in March 2026, and as of November 2026 the latest version is 1.2.0; it understands more than thirty ingress-nginx annotations.
Gateway API itself is moving fast too. v1.5 (February 2026) moved ListenerSet, TLSRoute and the HTTPRoute CORS (Cross-Origin Resource Sharing) filter into the standard channel; v1.6 (June 2026) moved TCPRoute and UDPRoute to standard; and v1.7, released on 29 October 2026, made HTTPRoute retries and BackendTLSPolicy's additional TLS (Transport Layer Security) options standard. What it means for Bookstore: you can now write a retry policy on the HTTPRoute from the sixth post in the standard channel, without implementation-specific annotations. Which features you can use depends on which Gateway API version and channel your implementation supports; check the implementation's conformance report before installing.
Production Notes
Before using a new feature, answer three questions: does your cluster's version support it (managed clusters are usually a minor or two behind), is the feature GA (beta features can change in the next release), and does your manifest still go to a cluster running an older version (the same manifest might hit 1.37 in staging and 1.35 in prod). An unsupported field can be silently dropped by the API server; check with kubectl apply --dry-run=server -o yaml that the field really stays. This post is a snapshot: the table will change as Kubernetes 1.38 and later Gateway API versions come out. To keep up, the release posts on kubernetes.io and each release's "deprecations and removals" post are enough; before an upgrade, pluto from the eighteenth post and the deprecated API metric from the seventeenth post are still the two most reliable checks.
Common Pitfalls
Running a native sidecar with a shell command that ignores SIGTERM and adding 30 seconds to every Job. Keeping the sidecar's log in a buffer and losing it to SIGKILL. Forgetting that the preStop sleep is deducted from terminationGracePeriodSeconds and leaving the app no time to shut down. Assuming in-place resize is reflected automatically in the .NET runtime. Applying a new field to an older cluster and not noticing it was silently dropped. Ignoring the externalIPs and Endpoints warnings. Leaving the retired ingress-nginx in place because "it works"; working doesn't mean patched. And guessing from documentation whether a feature is on in your cluster, when the API server's metric gives the definitive answer.
A Small Experiment
★☆☆ Read the kubernetes_feature_enabled metric on your own cluster and list the state of the features in this post's table. If you have a managed cluster, produce the same list there and compare.
★★☆ Add a log shipper to the Bookstore API as a native sidecar and watch the shutdown order while the pod is deleted: in which order and at which seconds do the preStop sleep, the app's ApplicationStopping log and the sidecar's exit happen?
★★★ Add a retry policy to the HTTPRoute in the Gateway setup from the sixth post, and try it with a Bookstore API endpoint that produces errors, such as /api/diag/break-liveness. Which Gateway API v1.7 features does your Gateway implementation support? Verify from its conformance report.
With this post the Kubernetes series is complete. Starting from what a pod is, we moved Bookstore into the cluster step by step through Deployments, Services, Gateway API, configuration, health probes, resources and the .NET GC, graceful shutdown, scaling, storage, Jobs, Kustomize, security, debugging and the move to a managed cluster; and in the last two posts we read the versions of the past and the present. The natural next stops are the upcoming Helm, GitOps and Argo CD (CD: Continuous Delivery), and Observability series: packaging the app, deploying it to the cluster from Git, and seeing what happens in production.