Your ingress controller is officially unmaintained, and the replacement API just had its biggest stabilization release ever. In March 2026 the community ingress-nginx controller reached end of life — no more releases, no more security patches — while a month earlier Gateway API v1.5 had shipped as a graduation-only release: zero new CRDs, six Experimental features promoted to the Standard channel. If you run Ingress plus NGINX on machines you own, this is the moment the migration math flips. Here is exactly what became stable, what it means for an ingress2gateway cutover, and the checklist to get there.
Why this migration has a deadline now
Two dates set the context. On November 11, 2025, Kubernetes SIG Network announced the retirement of the ingress-nginx controller, with best-effort maintenance ending in March 2026. Note the precise scope: the Ingress resource API stays GA, feature-frozen, and fully supported. What died is the controller most self-hosted clusters actually run — the thing turning your Ingress objects into NGINX config. Every month you stay on it past March, you absorb unpatched CVE risk yourself, and compliance tooling (SOC 2, PCI-DSS, HIPAA scanners) has started flagging EOL installations.
The other date is February 27, 2026, when Gateway API v1.5 shipped — SIG Network's self-described "biggest release yet," devoted entirely to moving existing Experimental features to Standard. The announcement (published on the Kubernetes blog April 21, with a v1.5.1 patch already out) also switched the project to a release-train model borrowed from SIG Release, so future graduations land on a cadence instead of whenever a feature happens to be ready. Seven implementations were already fully conformant at announcement time: Agentgateway, Airlock Microgateway, GKE Gateway, HAProxy Ingress, kgateway, NGINX Gateway Fabric, and Traefik Proxy. And unlike a Kubernetes minor version, you don't need a cluster upgrade to adopt it — anything on Kubernetes 1.30 or later can install the v1.5 CRDs.
In that same window, SIG Network shipped ingress2gateway 1.0 (March 20, 2026): a stable migration assistant that converts Ingress resources — including 30+ common ingress-nginx annotations — into Gateway and HTTPRoute manifests. The EOL stick and the stable-migration carrot arrived in the same month. That combination is why a fleet still fronted by Ingress and NGINX should treat this as a scheduled project now, not a someday refactor.
The six things v1.5 made stable (and which ones your migration touches)
Here is the core deliverable: every v1.5 promotion, with an honest rating of whether an Ingress-to-Gateway migration on a self-hosted fleet actually cares.
| # | Graduated to Standard | What it is | Migration relevance |
|---|---|---|---|
| 1 | ListenerSet (GEP-1713) | Listeners defined independently of the Gateway and merged onto it; breaks the 64-listener ceiling | Medium — matters once tenant custom domains multiply listeners |
| 2 | TLSRoute (GEP-2643) | SNI-based TCP/TLS routing with Passthrough and Terminate modes | High — the stable answer for non-HTTP TLS, e.g. tenant databases or MQTT |
| 3 | HTTPRoute CORS filter (GEP-1767) | Declarative CORS (allowOrigins, allowMethods, allowCredentials, maxAge, …) as a route filter | High — replaces the enable-cors annotation cluster directly |
| 4 | Client certificate validation (GEP-91) | Frontend mTLS: Gateway validates client certs against CA bundles, with AllowValidOnly / AllowInsecureFallback modes | Medium — stable contract for mTLS front doors, if you terminate any |
| 5 | Backend TLS origination cert selection (GEP-3155) | tls.backend.clientCertificateRef on the Gateway for upstream mTLS identity | Low for most migrations — backend mTLS is a day-2 hardening step |
| 6 | ReferenceGrant → v1 | Unchanged for over a year; now under the GA no-breaking-changes contract | High — cross-namespace routing now rests on a stable API |
Three of these deserve a closer look from a migration standpoint.
TLSRoute is the one that unblocks real cutovers. Before v1.5, any non-HTTP TLS traffic — a tenant Postgres exposed over TLS, an MQTT endpoint, passthrough for end-to-end-encrypted streams — sat on an Experimental API. Now SNI routing with explicit Passthrough (gateway never sees plaintext or private keys) versus Terminate (centralized cert management at the gateway) modes is a v1 contract. One sharp edge, straight from the release notes: if you install the v1.5 Standard CRDs over v1.4-or-earlier Experimental, existing Experimental TLSRoutes stop working, because v1alpha2/v1alpha3 are not served by the Standard bundle. Either stay on the Experimental channel or migrate those manifests to v1 first. Budget that step explicitly.
The CORS filter kills the most copy-pasted annotation block in ingress-nginx history. The nginx.ingress.kubernetes.io/enable-cors family (cors-allow-origin, cors-allow-methods, cors-allow-credentials, cors-max-age, …) becomes a typed CORS filter on the HTTPRoute rule, including wildcard-subdomain origins like https://*.bar.com. This is the rare migration subtask that is strictly better on the other side: schema-validated fields instead of stringly-typed annotations that fail silently on typos.
ReferenceGrant going v1 matters more than it looks. The "route in namespace A may point at a Service in namespace B" permission object is load-bearing for any multi-tenant layout — a shared platform Gateway routing to per-tenant Services, for example. It hadn't changed in over a year, and v1.5 finally put the GA contract behind that stability. Cross-namespace backends are no longer an experimental privilege.
ListenerSet is the sleeper for PaaS-shaped fleets: today your platform team owns one Gateway object and every tenant custom domain means editing it. ListenerSets let each tenant namespace contribute its own listeners (own hostname, own cert) onto the shared Gateway, and lift the 64-listener cap that would otherwise force a Gateway split as custom domains grow. You don't need it on day one of the migration, but it is the stable primitive your tenant-domain automation should target.
Worked example: one Ingress with TLS becomes a Gateway plus HTTPRoute
Take the most typical object on a self-hosted fleet: an ingress-nginx Ingress fronting one app, with a cert-manager annotation and a TLS block. This is the shape ingress2gateway 1.0 was built to convert:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
namespace: tenants-acme
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts: [shop.acme.example.com]
secretName: shop-acme-tls
rules:
- host: shop.acme.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: shop
port:
number: 80Running ingress2gateway print --input-file ingress.yaml --providers nginx produces the Gateway-plus-HTTPRoute skeleton: a Gateway with an HTTP listener and an HTTPS listener carrying the TLS block, and an HTTPRoute matching the host and path prefix. What the tool emits faithfully is the routing shape. What you must hand-finish:
- Certificate issuance moves to the gateway-shim model. The
cert-manager.io/cluster-issuerannotation has no Gateway API equivalent on the route itself. With cert-manager 1.20+ (February 2026, timed for the ingress-nginx EOL), you enable the Gateway API gateway-shim (--enable-gateway-api) so cert-manager reads TLS config from Gateway listeners and issues theshop-acme-tlsSecret the same way. Verify the shim is actually enabled — a converted manifest with acertificateRefspointing at a Secret nothing issues is the most common silent break in this migration. - The TLS mode choice is now yours to make deliberately. ingress-nginx always terminated TLS. On Gateway API, HTTPS listeners terminate by default, but if this tenant later needs end-to-end encryption to the pod, v1.5's stable TLSRoute Passthrough mode is the sanctioned path — no experimental channel required.
- DNS and hostnames stay your job.
external-dnsships a Gateway API source that reads hostnames from HTTPRoutes the way it read them from Ingresses. Point it at the new source during the cutover window or tenant custom domains go stale while routes look healthy.
For a PaaS managing many tenant custom domains, step back and see the pattern this example generalizes into: one shared Gateway (owned by the platform, in the infra namespace) plus one HTTPRoute per tenant app (owned by the tenant namespace, attached via parentRefs). Post-v1.5, the cross-namespace attachment leans on stable ReferenceGrant, and per-tenant listeners can graduate to ListenerSets when the 64-listener ceiling approaches. That is the target architecture the migration buys you — not just "the same routes with different YAML."
Where ingress2gateway 1.0 output still needs a manual pass
The tool converts structure, not intent. Before any converted manifest touches a production fleet, walk this checklist:
- Untranslated annotations. Thirty-plus supported annotations sounds generous until you meet annotation number thirty-four. Audit every
nginx.ingress.kubernetes.io/*annotation in the fleet first: rate limiting, auth-request, rewrite-target, and session affinity all have Gateway API stories (mostly via implementation-specific policies or extension filters), but none of them survive conversion automatically. Anything the tool skips, it drops silently into a comment or ignores — diff every output. - TLS mode per listener. As above: the converter assumes termination. Any tenant relying on TCP passthrough semantics (some gRPC-with-own-certs setups, non-HTTP SNI routing) needs an explicit TLSRoute in Passthrough mode, which is now stable — good — but still a hand-authored resource the converter will not invent for you.
- Cross-namespace backends. If any Ingress routes to a Service in another namespace (via the old tricks) or your target layout puts the Gateway in
infraand routes in tenant namespaces, add explicit ReferenceGrants. The converter emits same-namespace assumptions; the v1 ReferenceGrant API is the stable fix, but you write it. - Implementation conformance deltas. "v1.5 conformant" does not mean "identical." The seven conformant implementations at release time cover the core profiles, but extended behaviors (exact CORS edge cases, mTLS modes, ListenerSet merge semantics) vary. Pick one implementation for the fleet — Envoy Gateway and kgateway are the usual self-hosted shortlist, NGINX Gateway Fabric if you want to keep an NGINX data plane — and run its conformance report against the features your converted manifests actually use.
- The v1alpha2 residue scan. If the fleet ever ran the Experimental channel (for early TLSRoute, TCPRoute, or UDPRoute experiments),
kubectl getevery experimental resource before installing the v1.5 Standard bundle, and migrate stragglers tov1. Installing Standard over old Experimental orphansv1alpha2-stored objects — the release notes warn about this explicitly, and it is exactly the kind of failure that looks like "the migration broke routing" at 2 a.m.
The cutover runbook for a small fleet
For a handful of nodes on owned hardware — the CAPH-on-Hetzner shape, say — the safe sequence is boring on purpose. Boring is the point; there is no spare on-call bench to absorb a clever cutover gone wrong.
- Install the v1.5 Standard-channel CRDs (
standard-install.yamlfrom the v1.5.0 or v1.5.1 release) on a non-production cluster first. Confirm nov1alpha2TLSRoutes exist anywhere (kubectl api-resourcesplus a grep over git). - Deploy your chosen Gateway controller alongside ingress-nginx. Both data planes coexist fine — different Services, different load-balancer IPs or NodePorts. Nothing routes through the new Gateway until you point DNS at it.
- Convert service by service, lowest-risk first. Run
ingress2gateway, apply the manual-pass checklist above, and stand up the Gateway plus HTTPRoute next to the live Ingress. Validate with a hosts-file override or a staging hostname before touching DNS. - Shift traffic per hostname. Move one tenant custom domain's DNS record to the new load balancer address, watch error rates and TLS issuance (cert-manager Certificate readiness, external-dns TXT churn), then proceed domain by domain. Keep the Ingress objects in place during the whole window — instant rollback is a DNS revert.
- Decommission. Only after every hostname has served cleanly through a full certificate-renewal cycle on the new stack, delete the Ingress objects and uninstall the ingress-nginx controller. Unpatched-but-uninstalled beats unpatched-and-running.
One scoping note: this runbook assumes HTTP(S) workloads, which is nearly every tenant app on a git-push PaaS. If the fleet also serves raw TCP/UDP (game servers, DNS, custom protocols), read the next section before scheduling — v1.6 changed that half of the board.
What v1.6 already added, and what is still experimental
The release train kept moving. Gateway API v1.6.0 shipped June 30, 2026 (SIG Network write-up August 3): TCPRoute and UDPRoute graduated to v1 on the Standard channel, and their v1alpha2 versions were deprecated in the same release — the removal clock is ticking, so any fleet that experimented with L4 routes should migrate those manifests to v1 now rather than during the next EOL scramble. Ecosystem uptake is already visible: Cilium 1.20 (September 14, 2026) jumped from Gateway API v1.4 straight to v1.6, adding TCPRoute/UDPRoute alongside CORS filters and ListenerSets.
What remains experimental is a shrinking island: session persistence, some mesh-adjacent policy attachment, and the newest inference-routing extensions. The honest boundary for planning purposes is this — everything an Ingress replacement needs (HTTP routing, TLS termination and passthrough, CORS, cross-namespace grants, L4 routes, client-cert validation) is now Standard. If your migration plan still cites "Gateway API isn't stable enough" as a deferral reason, that reason expired in February.
The deeper shift is architectural. Ingress was one object, one controller, one team's annotations. Gateway API is a graduated contract surface: platform teams own Gateways, tenant namespaces own routes and ListenerSets, and the conformance suite — not a vendor's annotation docs — defines what portable means. v1.5 is the release where that contract got wide enough to stand on. Migrate onto the stable surface now, while ingress2gateway 1.0 and a living community remember the old world well enough to automate leaving it.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



