Ingress-NGINX didn't quietly fade out. It went out with a CVSS 9.8 remote-code-execution bug, a failed replacement project, and a follow-up vulnerability disclosed two months after the repository was archived — with no patch coming, ever, for anyone still running it.
If you're still routing traffic through ingress-nginx in mid-2026, the retirement isn't a countdown anymore. It already happened. The only open question is what your migration actually carries over automatically, and what you have to rebuild by hand before a bad translation takes down every tenant on your cluster at once.
The clock already ran out
The timeline is worth having in order, because "Ingress-NGINX is being deprecated" undersells how this actually went down:
- March 2025 — "IngressNightmare" (CVE-2025-1974), a CVSS 9.8 unauthenticated remote-code-execution flaw in the admission webhook, exposed a project almost nobody was resourced to maintain. It was the moment the sustainability problem stopped being theoretical.
- November 2025 — Kubernetes' contributors publicly announced the retirement plan. The proposed successor, a new controller called InGate, was supposed to give the project a path forward.
- January 29, 2026 — The Kubernetes Steering Committee and Security Response Committee issued a joint statement confirming the wind-down, after InGate failed to mature into a real replacement and was retired alongside it.
- February 2026 — Four more HIGH-severity CVEs were disclosed in the weeks before the deadline. They got patched. It was the last batch that would.
- March 2026 — Ingress-NGINX officially retired. The repository is archived and read-only: no releases, no bugfixes, no security patches, for anyone.
- May 2026 — An NGINX "Rift" heap buffer overflow was disclosed in code paths that affect retired Ingress-NGINX builds. It will not be patched. Anyone still running the controller in production is carrying that exposure indefinitely.
That last date is the one that matters for anyone reading this in July 2026: the risk isn't hypothetical anymore, it already materialized once, and the project has no mechanism left to respond to the next one.
What Ingress2Gateway 1.0 actually does
The Kubernetes project's answer to "how do I get off this thing" is ingress2gateway, which hit its 1.0 release on March 20, 2026 — the same month the controller it migrates you off of went dark. It's a CLI that reads your existing Ingress objects plus their ingress-nginx-specific annotations, and emits Gateway, HTTPRoute, and (where needed) TLSRoute resources targeting the now-stable Gateway API.
The important design decision in 1.0 isn't the translation itself — it's what happens when translation isn't possible. Anything the tool can't convert gets dropped from the generated output and reported to stderr, rather than silently omitted. That's the difference between a migration assistant and a migration trap: you get a list of exactly what didn't make it across, instead of finding out three tenant complaints later.
Here's what does cross cleanly, translated straight into standard Gateway API fields:
| Ingress-NGINX annotation | Gateway API equivalent |
|---|---|
use-regex / path matching | HTTPRoute PathPrefix/RegularExpression match |
rewrite-target | HTTPRoute URLRewrite filter |
enable-cors + CORS headers | HTTPRoute CORS filter (stable as of Gateway API v1.5) |
proxy-body-size | HTTPRoute / backend policy field |
proxy-read-timeout / proxy-send-timeout | HTTPRoute timeout fields |
ssl-redirect / force-ssl-redirect | HTTPRoute RequestRedirect filter |
ssl-passthrough | TLSRoute (Standard channel) |
from-to-www-redirect | HTTPRoute redirect filter |
app-root | HTTPRoute redirect filter |
canary-weight / canary-by-header | HTTPRoute weighted backendRefs / header-match rules |
That's roughly 30 annotations covering CORS, backend TLS, regex matching, path rewrites, buffer sizing, IP-range control, and canary traffic splitting — the bulk of what a typical Ingress manifest actually uses. For a large share of straightforward HTTP routing, running ingress2gateway really is close to a one-command migration.
The two gaps the tool can't paper over
The stderr warnings are where the real inventory work happens, and two categories show up in almost every non-trivial cluster.
Session affinity has no portable equivalent. nginx.ingress.kubernetes.io/affinity (and the related session-cookie-name, session-cookie-path settings) don't map onto any field in the core Gateway API spec. There is no HTTPRoute.spec.stickySession. If your app relies on sticky sessions — and most stateful WebSocket or in-memory-session apps do — you need an implementation-specific extension, like Envoy Gateway's BackendTrafficPolicy, layered on top of the translated HTTPRoute. Which extension you need, and whether your chosen Gateway controller even ships one, is a decision ingress2gateway cannot make for you.
Custom snippets have no translation path at all. If any Ingress in your fleet uses configuration-snippet to inject raw NGINX directives or Lua, that logic simply isn't representable as a Gateway API object — standard or experimental. It has to be re-implemented at a different layer entirely: a WASM plugin, an ext_proc filter, a controller-specific policy CRD, or moved out of the ingress layer into the application itself. There's no CLI flag that fixes this. It's a rewrite, not a conversion.
Neither gap is a bug in ingress2gateway — they're honest reflections of the fact that Gateway API is a narrower, more portable spec than NGINX's annotation surface ever was. The tool doing the honest thing (report and drop, rather than guess) is the right design. It just means the annotation inventory is the migration plan, not a formality before one.
Why one cluster's migration and a PaaS's migration are not the same clock
If you're migrating a single app's Ingress, a dropped session-affinity rule is a bug you find in staging and fix before it ships. If you're operating a self-hosted PaaS routing dozens or hundreds of tenants' custom domains through the same ingress-nginx deployment, running ingress2gateway once against the whole fleet and applying the output is a single event that touches every tenant's routing simultaneously. A silently-dropped sticky-session rule doesn't break one team's staging environment — it breaks session persistence for every tenant relying on it, at the same moment, in production.
That asymmetry is the actual argument for treating this as a staged rollout rather than a cutover:
- Run the tool per-namespace or per-tenant batch, not once against the entire fleet — so a translation gap surfaces against ten tenants instead of all of them.
- Diff the generated
HTTPRoute/Gatewayobjects against the existingIngressbehavior before applying anything, specifically checking for stderr warnings on affinity and snippet annotations. - Keep the old
Ingressobjects live in parallel behind a feature-flagged subset of traffic until the newGatewayroute has been validated against real tenant traffic, not just a smoke test. - Only flip DNS/load-balancer routing for a tenant once its specific annotation set has been confirmed clean — a tenant using nothing but path rewrites and CORS is a same-day migration; a tenant leaning on session affinity and Lua snippets needs a policy CRD in place first.
This is exactly the kind of control-plane concern a git-push PaaS should absorb on a tenant's behalf rather than leaving every tenant to independently discover which of their own annotations survive. On bex, custom domains and TLS termination sit behind the platform's own routing layer specifically so an underlying ingress migration like this one is the platform's job to get right once, not something each deployed app has to individually re-litigate against ingress2gateway's stderr output.
What "done" looks like on the other side
Gateway API itself isn't standing still while this migration happens. Version 1.5, released February 27, 2026, moved another batch of long-experimental features into the Standard channel: the CORS filter (letting enable-cors translate to a first-class spec field rather than a workaround), TLSRoute reaching Standard for ssl-passthrough cases, and ListenerSet for cleaner multi-tenant listener management. The gap between "what Ingress-NGINX could do with annotations" and "what Gateway API can do natively" keeps closing — but as of mid-2026, session affinity and custom request-processing logic still aren't in it, and won't be solved by waiting for the next release.
The honest read for anyone still running the retired controller: ingress2gateway will do most of the mechanical work correctly and tell you exactly where it stopped. The stderr output is the punch list. Whether that punch list is a same-day fix or a multi-week policy-CRD project depends entirely on how many of your tenants' Ingress manifests lean on the two things Gateway API was never designed to make portable.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with custom domains and TLS handled by the platform's own routing layer instead of each tenant's own ingress config. Star the repo on GitHub or deploy your first app today.
Sources
- Ingress NGINX Retirement: What You Need to Know — Kubernetes Contributors
- Ingress NGINX: Statement from the Kubernetes Steering and Security Response Committees
- HeroDevs: Ingress NGINX End of Life (March 2026) — Risks, Migration Paths, and Support Options
- Datadog Security Labs: Kubernetes project issues warning on Ingress NGINX retirement
- Announcing Ingress2Gateway 1.0: Your Path to Gateway API
- kubernetes-sigs/ingress2gateway on GitHub
- Gateway API v1.5: Moving features to Stable — Kubernetes
- Gateway API: Migrating from Ingress
- agentgateway: Migrate from Ingress-NGINX



