On March 24, 2026, the most widely used Ingress controller in the Kubernetes ecosystem became read-only. The kubernetes/ingress-nginx repository was archived after its final releases shipped on March 19, and there will be no new features, no bug fixes, and — the part that should end every "we'll migrate later" discussion — no more CVE patches (retirement announcement, migration guide).
If your fleet still terminates tenant traffic through ingress-nginx, you are now running an internet-facing proxy whose next IngressNightmare-class RCE (CVE-2025-1974) will never get a fix. The good news: four days before the archive, SIG Network shipped ingress2gateway 1.0 (March 20, 2026), a stable tool that converts existing Ingress objects — annotations included — into Gateway API resources instead of forcing you to hand-write every tenant's HTTPRoute from scratch.
This post is the migration triage for a fleet operator: what the scripted pass converts cleanly, what still needs hand-migration, and the five behavioral gotchas that cause silent outages when you convert syntax but not semantics.
The migration on one page
Run one command and you learn which bucket every tenant Ingress falls into:
ingress2gateway print --providers ingress-nginx --all-namespaces 2>&1 | tee migration-preview.yaml
ingress2gateway print --providers ingress-nginx --all-namespaces 2>&1 | grep "WARNING"The tool prints converted Gateway and HTTPRoute manifests to stdout and logs a WARNING for every annotation it cannot convert. Those warnings are your hand-migration backlog. Everything without a warning is bucket A:
| Bucket | Contents | Action |
|---|---|---|
| A. Converts automatically | Host/path routing, TLS termination, rewrite-target → URLRewrite filter, canary/canary-weight → weighted backendRefs | Review the printed manifests, apply |
| B. Needs vendor extensions | auth-url/auth-response-headers → ext-auth policy, limit-rps/limit-connections → rate-limit policy, session affinity, CORS | Hand-write per your chosen implementation's CRDs |
| C. No equivalent | configuration-snippet lines that depend on nginx internals, modsecurity-* WAF rules | Re-implement (Envoy patch policy, separate WAF) or drop |
This three-bucket framing follows Kazu's annotation walkthrough, which pairs every category with before/after manifests in shinagawa-web/ingress-nginx-to-gateway-api. One subtlety worth knowing up front: bucket C is judged line by line, not annotation by annotation. Most configuration-snippet contents, broken down line by line, land in bucket A (a response header becomes a standard filter) or bucket B (a request-ID becomes an Envoy patch policy). Only directives that depend deeply on nginx internals are truly unmigratable.
What makes the scripted pass viable now, versus the 0.x era, is what 1.0 landed:
- 30+ supported ingress-nginx annotations, up from 3 in the previous release — the common cases (rewrites, canary weights, TLS, CORS-adjacent settings) convert without hand-editing.
- A stable
printworkflow that also emits GatewayClasses, TLSRoutes, TCPRoutes, and ReferenceGrants where the input implies them, not just Gateways and HTTPRoutes. - Pluggable emitters for vendor-specific output (e.g.
--emitter envoy-gateway), so bucket B items can come out as your implementation's extension resources instead of stopping at standard CRDs.
The 1.0 scope is documented in the release coverage from March 31; providers include ingress-nginx and kong, so Kong-origin Ingress objects get the same scripted first pass.
Why the retirement happened, and what replaced it
The cause was not technical. The November 2025 retirement announcement was blunt: not enough maintainers, with effectively a skeleton crew carrying the most-deployed Ingress controller for years. A Steering Committee and Security Response Committee statement followed on January 29, 2026, and then the clock ran fast:
| Date | Event |
|---|---|
| Nov 11–12, 2025 | Retirement announced |
| Jan 29, 2026 | Steering Committee + SRC statement |
| Feb 27, 2026 | "Before You Migrate" behaviors post + Gateway API 1.5 (ListenerSet, HTTPRoute CORS filter) |
| Mar 19, 2026 | Final ingress-nginx releases |
| Mar 20, 2026 | ingress2gateway 1.0 |
| Mar 24, 2026 | Repository archived read-only |
No successor was named — just a recommendation to migrate to Gateway API. Even the InGate successor experiment ended up archived under kubernetes-retired. That reads as an abdication until you see the reasoning: the ecosystem already had five-plus conformant Gateway API implementations, so blessing one successor would have been picking a winner rather than filling a gap.
Gateway API changes the ownership model, which matters for how you assign the migration work. Ingress gave you one flat object the cluster admin managed end to end. Gateway API splits it into three roles: the infrastructure team owns the GatewayClass, platform ops owns the Gateway (listeners, TLS, attached policies), and developers own HTTPRoute objects that attach to it. For a self-hosted PaaS, that split is a feature: your controller can own the Gateway while each tenant's route stays a small, reviewable object.
Five behaviors that break silently if you only convert syntax
SIG Network's February 27 post documents five ingress-nginx quirks where a structurally correct conversion still causes outages. Audit your fleet for these before you apply anything the tool prints — ingress2gateway converts declared intent, not accidental behavior your clients may depend on:
| # | Surprising behavior | Outage if ignored | Gateway API fix |
|---|---|---|---|
| 1 | Regex matches are prefix-based and case-insensitive (/[A-Z]{3} matches /uuid) | Silent 404s — Gateway API regex is full-match, case-sensitive | Widen the pattern explicitly (/[a-zA-Z]{3}.*) |
| 2 | use-regex: "true" on one Ingress makes all paths for that host regex, across all Ingresses | Exact paths that "worked" via accidental regex now 404 | Audit every Ingress sharing the host; convert intended-regex paths only |
| 3 | rewrite-target silently implies use-regex, with all of #2's side effects | Same as #2, with no regex annotation to grep for | Same audit — search for rewrite-target, not just use-regex |
| 4 | /my-path gets an automatic 301 to /my-path/ (Exact or Prefix, non-regex) | Clients following the redirect now get 404 | Add an explicit RequestRedirect filter with ReplaceFullPath |
| 5 | URL normalization before matching (./.. collapsed, // deduplicated) | Mostly safe — Istio, Envoy Gateway, and Kgateway normalize ./.. by default | Verify against your implementation's docs |
Items 2 and 3 are the ones that bite fleets specifically. A tenant typo like path: /Header with pathType: Exact returns 200 today because another tenant's Ingress for the same host set use-regex — and after migration it 404s. No per-object converter can flag that; only a host-level audit across all namespaces catches it. That is the concrete reason the scripted pass is step one, not the whole migration.
Worked runbook: scripted pass to zero-downtime cutover
The production-safe shape is a parallel run: both stacks serve, DNS shifts gradually, ingress-nginx retires last. End to end:
1. Install the tool. Either flavor pins the 1.0 release:
go install github.com/kubernetes-sigs/ingress2gateway@v1.0.0curl -LO https://github.com/kubernetes-sigs/ingress2gateway/releases/download/v1.0.0/ingress2gateway-linux-amd64
chmod +x ingress2gateway-linux-amd64
sudo mv ingress2gateway-linux-amd64 /usr/local/bin/ingress2gateway2. Print, don't apply. Generate the conversion and collect the backlog in one pass:
ingress2gateway print --providers ingress-nginx --namespace production > gateway-resources.yaml
ingress2gateway print --providers ingress-nginx 2>&1 | \
grep "WARNING.*annotation" | sort -u > unsupported-annotations.txtRead every WARNING as a ticket: auth-url becomes an ext-auth policy, limit-rps a rate-limit policy, modsecurity-* a separate WAF decision. If you have already chosen your target, re-run with its emitter (e.g. --emitter envoy-gateway) to get extension resources drafted too.
3. Install the Gateway API foundation. Apply the standard-channel CRDs, then your chosen implementation and its GatewayClass:
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml
kubectl get gatewayclassOne caution carried over from early-2026 field reports: installing Gateway API CRD v1.5.x under Istio 1.28/1.29 crashed istiod through API field mismatches, so several guides pin v1.4.0 for Istio fleets. Check your implementation's supported-CRD matrix before reaching for the newest bundle — "latest CRDs" is not automatically the safe pick during a migration window.
4. Apply routes alongside, not instead. Create the Gateway (listeners, TLS via your existing cert-manager setup), apply the converted HTTPRoutes, and verify against the Gateway's address before touching DNS. Keep ingress-nginx serving production throughout.
5. Shift traffic gradually. Move DNS weights (or a test hostname first), watch 4xx/5xx rates per route — the section above tells you which 404s mean "behavioral gap" rather than "bad conversion" — then drain ingress-nginx and delete the old Ingress objects only after the new stack has served a full traffic cycle clean.
Picking the target implementation
Writing an HTTPRoute does nothing until some controller reads it, so the target choice is part of the migration. Nobody can pick it for you sight unseen, but a fleet operator can narrow the field fast with three questions, drawing on the four-way comparison (Envoy Gateway, Istio, NGINX Gateway Fabric, Traefik):
- Do you already run a mesh? If yes, Istio as a pure Gateway API controller (no sidecars required) reuses operational knowledge you have. If no, installing a whole mesh stack just to terminate edge traffic is the heaviest option on the table.
- How much bucket B do you have? Auth, rate limiting, and session affinity live in Extended or implementation-specific CRDs — Envoy Gateway's
SecurityPolicy, Istio'sAuthorizationPolicy— and the moment you write one, you are locked to that implementation. Envoy Gateway (CNCF incubating) and Istio (CNCF graduated) lead on extension maturity; Traefik and NGINX Gateway Fabric route the basics equally well but their vendor extensions are thinner. Size your bucket B before choosing. - How much nginx muscle memory matters? NGINX Gateway Fabric shares the nginx data plane, which makes it the gentlest behavioral landing — but it is F5's separate Gateway API project, not a continuation of ingress-nginx, and ships at a lower cadence. Kong deserves a look for the same "one codebase answers both APIs" reason: its controller serves classic Ingress and HTTPRoute side by side, which simplifies the parallel-run phase.
Two escape hatches exist if Gateway API itself is more migration than you can schedule right now: Traefik's Kubernetes provider reads ingress-nginx annotations directly, letting you swap the controller without rewriting objects — and HAProxy's ingress controller ships a Gateway API mode for a staged move. Treat those as bridges, not destinations; the ecosystem's conformance energy is all behind Gateway API now.
What to do this week
The deadline framing is over — the repository is archived and every week on ingress-nginx is unpatched-CVE exposure. The scripted pass is what makes "this week" realistic instead of aspirational:
- Inventory annotations across all namespaces (
kubectl get ingress -Aplus the WARNING grep above) and sort them into buckets A/B/C. - Run the scripted pass into a file, review the printed Gateways and HTTPRoutes, and turn every WARNING into a bucket B/C ticket with an owner.
- Audit the five behaviors per host, not per object — especially
use-regex/rewrite-targetleakage across tenants sharing a hostname. - Pick the target implementation using your bucket B size, then stand up CRDs + Gateway in parallel with production.
- Cut over one low-risk host first, watch 4xx rates with the behavior table open, then schedule the fleet-wide DNS shift.
The teams that will hurt are the ones hand-writing HTTPRoutes from scratch under pressure six months from now, after the first unpatched CVE drops. The tool is stable, the behaviors are documented, and the conversion of the common 80% is one command. Run it now while the migration is still a planned project instead of an incident response.
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.



