Skip to main content

Ingress-NGINX Is Retired: Your Gateway API Migration Playbook for the Rest of 2026

11 min readDora NodaDora Noda
Share
On this page

Six months ago, the most widely deployed Ingress controller in Kubernetes history stopped receiving security patches. Not a deprecation warning, not a slowed release cadence — a full retirement. If your edge still runs ingress-nginx today, every request your tenants serve passes through code that will never be fixed again.

The deadline the ecosystem spent a year warning about — March 2026 — has come and gone. The repositories are read-only. The maintainers have moved on. And the vulnerability reports that used to end with "upgrade to the fixed version" now end with nothing at all. This post is the playbook for the rest of 2026: what the retirement actually means, the five translation traps that break naive migrations, which annotations survive the move to Gateway API and which need a rewrite, how to pick an implementation for machines you own, and the TLS cutover checklist to get there safely.


The short version of the timeline, so the urgency is concrete:

DateWhat happened
Nov 11, 2025SIG Network and the Security Response Committee announce the retirement: best-effort maintenance until March 2026, then nothing
Jan 29, 2026A rare joint statement from the Steering Committee and the SRC: check your clusters now, start planning migration
Early Feb 2026A batch of new CVEs lands — including config-injection flaws leading to remote code execution — patched in what are effectively the final releases
Feb 27, 2026Kubernetes publishes "Five Surprising Ingress-NGINX Behaviors You Need to Know" alongside Gateway API 1.5
Mar 20, 2026ingress2gateway 1.0 ships, translating 30+ annotations days before the deadline
March 2026Maintenance halts. No further releases, bugfixes, or security updates — ever
Sept 2026 (now)Your unmigrated edge has been running unpatched for half a year

One clarification that still confuses teams: the Ingress API is not going away. What retired is the community-maintained controller — the kubernetes/ingress-nginx project that grew from an example implementation into the default edge for countless clusters. You can keep the Ingress API with a different controller. But upstream's recommendation, and the direction of all new investment, is Gateway API.

Why find-and-replace breaks: five behaviors that don't survive translation

The single most useful migration document published this year is the February Kubernetes blog post on surprising ingress-nginx behaviors. Its core warning: a seemingly correct translation can still cause outages if it ignores the controller's quirks. Every one of these has a concrete outage mode.

#The surprising behaviorWhat breaks after naive migrationThe Gateway API fix
1Regex matches are prefix-based and case-insensitive/[A-Z]{3} matches /uuidRoutes that used to match return 404, because Envoy-based implementations do full case-sensitive matchesUse RegularExpression with explicit case handling, e.g. (?i)/[a-z]{3}.*
2use-regex on one Ingress silently regex-ifies all paths of that host across all IngressesExact: /Header (a typo for /headers) used to match; after migration it 404sAudit every path on regex-enabled hosts; fix typos or convert to explicit RegularExpression
3rewrite-target implies regex, with all of behavior 2's side effectsSame silent 404s, on hosts where nobody ever set use-regexURLRewrite filter does not infect neighboring matches — but you must still fix the latent typos it was masking
4A request missing a trailing slash gets an automatic 301 redirect (/my-path/my-path/)Clients depending on the redirect now get 404Add an explicit RequestRedirect filter rule for the slashless path
5URLs are normalized before matching (dot segments resolved, slashes deduplicated per RFC 3986)Depends on implementation — most Envoy-based ones normalize . and .. by default, but verify yoursCheck your implementation's normalization docs; don't assume parity

Behaviors 2 and 3 are the ones that bite experienced teams. Nobody remembers that three years ago someone added rewrite-target to one Ingress on a shared host, and that every Exact path on that host has secretly been a case-insensitive prefix match ever since. Before you convert anything, list every host with use-regex or rewrite-target anywhere on it and treat all of its paths as suspect.

The annotation inventory: what converts, what needs a rewrite

ingress2gateway 1.0 is the official translation tool, and the 1.0 release was a genuine leap — from 3 supported ingress-nginx annotations to more than 30, including CORS, backend TLS, regex matching, and path rewrites. Run it early; its warnings are your rewrite list. But go in knowing the two columns:

Converts cleanly (ingress2gateway handles these, verify the output):

  • rewrite-targetURLRewrite filter (ReplaceFullPath or ReplacePrefixMatch)
  • use-regexRegularExpression path matches (with the case-sensitivity caveats above)
  • enable-cors and friends → the HTTPRoute CORS filter, which graduated in Gateway API 1.5
  • canary / canary-weight → weighted backendRefs on a single rule — arguably cleaner than the annotation ever was

Has no Gateway API equivalent (each one is a real config rewrite, not a translation):

  • configuration-snippet, server-snippet, auth-snippet — arbitrary NGINX config injection. There is deliberately no counterpart; Gateway API does not expose proxy internals. Every snippet must be re-expressed as a native route feature or a vendor-specific extension (PolicyAttachment, BackendTrafficPolicy, or your implementation's CRDs), and some — custom Lua, exotic rewrite logic — need redesigning.
  • auth-url / external auth — no standard equivalent; each implementation wires external authorization differently.
  • Session affinity (affinity: cookie), custom load-balance algorithms, proxy-*-timeout tuning, custom log formats — all implementation-specific now. Check your chosen implementation's policy APIs before you promise parity.
  • defaultBackend fallbacks — there is no shared "default backend" concept. You must write an explicit lowest-precedence catch-all HTTPRoute (or accept your implementation's default error response). Drop this silently and unmatched hosts get a bare 404/500 with no custom error page — the outage mode nobody tests until a customer reports it.

A useful discipline: treat every ingress2gateway warning as a ticket, not a footnote. The tool tells you exactly which annotations it skipped. The teams that get hurt are the ones that convert the 90% that translates, ship it, and discover the skipped 10% was load-bearing.

Picking a Gateway API implementation for machines you own

Gateway API is a standard, not software — you still have to pick the controller and data plane that terminates your tenants' TLS. For a self-hosted fleet on owned hardware, the shortlist in late 2026 is stable: Envoy Gateway, kgateway, Cilium, Traefik, and NGINX Gateway Fabric. They all speak the same core APIs (Gateway, HTTPRoute, and — since v1.6 graduated them to Standard in August — TCPRoute and UDPRoute), so evaluate on operational axes instead of feature checklists:

AxisWhat to check before committing
ConformanceRead the implementation's published Gateway API conformance report. "Supports HTTPRoute" is not binary — timeout handling, regex semantics, and header manipulation vary. One implementation rejects unsupported fields loudly; another silently ignores them. Prefer loud.
Install footprintDoes it reuse something you already run (Cilium if it's your CNI, Istio if it's your mesh) or add a new control plane? On a small fleet, every new control plane is on-call surface.
TLS modelHow are listeners, SNI routing, and certificate rotation handled? Can tenants bring custom domains without touching the shared Gateway? (See ListenerSet below.)
Policy APIsWhere do your ex-snippet behaviors live now — rate limiting, auth, timeouts? Compare the maturity of each project's policy CRDs, not just its route support.
L4 needsIf you terminate non-HTTP traffic (databases, game servers, custom protocols), confirm TCPRoute/TLSRoute support and maturity — Standard-channel status in v1.5/v1.6 doesn't mean every implementation is equally far along.

There is no universal winner, and any post that names one is selling something. The right question is narrower: which implementation makes your current snippet inventory expressible with the least custom machinery? Answer that by mapping your top five snippets to each candidate's policy docs before you install anything.

The TLS cutover checklist

For a PaaS, the edge migration is really a TLS migration with routing attached. Ingress-nginx habituated everyone to per-Ingress TLS via annotations (cert-manager.io/cluster-issuer and friends). Gateway API splits the world: cluster-operator-owned Gateway resources hold listeners and certificates, while team-owned HTTPRoute resources attach to them. That split is cleaner — and it means your certificate automation needs re-plumbing, not just re-pointing. Validate each of these before you shift traffic:

  1. Listeners carry the certs now. Move every hostname's certificate from Ingress TLS blocks to Gateway listeners backed by cert-manager Certificate resources. Audit that every served hostname has a listener match — Gateway API will not serve a hostname no listener claims.
  2. Wildcard issuance still goes through DNS-01. If you issue *.tenant-region.yourplatform.com, that flow is unchanged conceptually (cert-manager Certificate + DNS-01 solver), but the resulting Secret is now referenced from the Gateway, not the Ingress. Verify renewal updates propagate to the data plane without a restart.
  3. Per-tenant custom domains become HTTPRoutes on the shared Gateway. Each customer.com CNAME-ing to you gets an HTTPRoute with a hostnames match — but its certificate needs a home. This is what Gateway API 1.5's ListenerSet is for: letting teams manage their own TLS on a shared Gateway instead of forcing every cert change through the cluster operator. cert-manager has been tracking ListenerSet support through 2026; confirm your cert-manager version handles your issuance pattern before cutover.
  4. HTTP-01 challenge routing must exist on the new Gateway. cert-manager's HTTP-01 solver creates challenge routes assuming Ingress semantics by default. Either keep an Ingress-class solver path alive during migration or configure the solver for your Gateway implementation — otherwise renewals fail silently mid-migration and you discover it via expiry alerts.
  5. Verify SNI and hostname coverage with a scan, not a spot check. Enumerate every hostname in every Ingress, then assert each one resolves to a listener plus an attached route on the new Gateway. The trailing-slash and normalization behaviors from section one mean "same hostnames" is necessary but not sufficient — replay production access logs against the new edge in staging and diff status codes.

Item 5 deserves emphasis because it catches everything above. Access-log replay is the closest thing this migration has to a proof: same requests in, same status codes out. Any 404 that wasn't a 404 before is a behaviors-2/3/4 regression hiding in your config.

Cutover runbook: dual-run, shift, delete

Don't flag-day the edge. The controllers coexist fine, which makes this a traffic-shifting problem rather than a forklift upgrade:

  1. Deploy the new Gateway alongside ingress-nginx. Same backends, different entry point. Nothing serves production through it yet.
  2. Mirror one low-risk hostname. A status page or internal tool — something with real traffic and forgiving users. Compare status-code distributions between old and new edges for at least a full business day.
  3. Shift DNS per hostname, slowest-first. CNAME or A-record one production hostname at a time to the new edge. After each shift, watch 4xx/5xx rates for that host specifically — global dashboards average away per-host regressions.
  4. Keep rollback boring. Leave ingress-nginx installed but scaled down (or DNS-pointed-away) until every hostname has baked on the new edge for at least one full certificate-renewal cycle. Rollback should be a DNS change, not a reinstall.
  5. Decommission with a scan. When the last hostname has moved, confirm with the same detection command the retirement post gave everyone — kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx — then remove the controller, its CRDs, and the legacy annotations. Dead edge config left in the cluster is how the next CVE finds you.

One thing not to do: reach for a community fork as a permanent plan. Forks buy calendar time at the cost of consolidating your edge on a shrinking maintenance base — the exact situation that forced the retirement. If a fork is your bridge, name the far side of the bridge and the quarter you cross it.

The edge you own is the edge you must maintain

The ingress-nginx retirement is the clearest demonstration in years of a self-hosting truth: owning the machine means owning the lifecycle of everything on it. A managed platform migrates your edge for you and tells you afterward. On your own fleet, the November 2025 announcement, the January joint statement, the February CVEs, and the March cutoff were all messages addressed directly to you — and the teams that treated them as such are done already.

If you're reading this in the second half of 2026 with ingress-nginx still terminating production TLS, the playbook above is the fastest safe route out: inventory the behaviors, translate what's translatable, rewrite what isn't, re-plumb TLS deliberately, and shift traffic hostname by hostname. The unmaintained edge doesn't get safer with time. It just gets older.

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.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex