Why did Gateway API win over Ingress?
Gateway API won because Ingress could not express real-world routing without vendor annotations. It reached GA in October 2023, is maintained by Kubernetes SIG Network, and models roles, protocols, and cross-namespace delegation as first-class API fields. The Ingress API is feature-frozen, and most major controllers now treat Gateway API as their primary surface.
The failure mode of Ingress was annotation sprawl. A production Ingress object for ingress-nginx routinely carried ten or more nginx.ingress.kubernetes.io annotations for rewrites, timeouts, body-size limits, and canary weights, none of them portable to any other controller. Ingress promised a standard and delivered a lowest common denominator plus a dialect per vendor. Gateway API fixed the underlying design problems:
- ▸Typed routing semantics. Header matching, traffic weighting, redirects, rewrites, and mirroring are spec fields with conformance tests, not free-text annotations.
- ▸Role separation. Cluster operators own Gateways; application teams own routes. Ingress mashed both into one object owned by nobody.
- ▸Protocol coverage. HTTP, gRPC, TLS, TCP, and UDP routes exist in the API instead of being bolted on.
Is the Ingress API actually going away?
No. The Ingress resource remains in Kubernetes and existing objects keep working. What is going away is investment: the API has been feature-frozen for years, and the ingress-nginx project, the most widely deployed controller, was publicly slated for retirement, with best-effort maintenance announced to wind down in early 2026. Verify the current status against the official Kubernetes blog before you plan.
That controller announcement, not API removal, is what changed migration math for most teams. If your edge depends on a controller whose CVE response is best-effort, you have a security problem regardless of how stable the API is. Treat the migration as you would any end-of-support event: inventory, plan, and move deliberately, typically over one or two quarters, not one weekend.
Which Gateway API resources replace what you have today?
Four kinds cover a first migration. GatewayClass replaces your controller choice, Gateway replaces the listener configuration that lived in controller flags and annotations, and HTTPRoute replaces Ingress rules. ReferenceGrant authorizes cross-namespace references, such as a route in an app namespace pointing at a TLS secret elsewhere.
Beyond those four:
- ▸GRPCRoute is in the standard channel and gives you method-level gRPC matching without HTTP/2 annotation tricks.
- ▸TCPRoute, UDPRoute, and TLSRoute live in the experimental channel, usable, but check your controller's conformance report first.
- ▸BackendTLSPolicy handles re-encryption to backends, a long-standing annotation swamp in Ingress.
- ▸Listeners on a Gateway declare port, protocol, hostname, and which namespaces may attach routes via allowedRoutes, the delegation mechanism Ingress never had.
Install the standard channel CRDs unless you have a concrete need; the experimental channel can introduce breaking changes between releases.
Which Gateway API controller should you choose?
Choose based on what you already run. Envoy Gateway or Cilium's built-in Gateway support suit greenfield clusters; Istio is the obvious choice if you already run its mesh; NGINX Gateway Fabric fits estates with NGINX operational muscle; cloud controllers (GKE Gateway, AWS, Azure) make sense when you want a managed data plane. Judge by conformance reports, not marketing pages.
Practical notes from migrations we have run:
- ▸Envoy Gateway is the CNCF-backed reference implementation on Envoy; its extension policies (BackendTrafficPolicy, SecurityPolicy) cover most ingress-nginx annotation use cases cleanly.
- ▸Cilium already terminates north-south traffic in its eBPF dataplane; if you run it as your CNI, enabling its Gateway support avoids an extra hop and an extra Helm release. Background in eBPF in Production: Rewiring Networking and Observability.
- ▸Istio gives you one API for mesh and edge, which simplifies mTLS at the boundary but couples upgrades.
- ▸Managed cloud gateways offload patching but typically lag the spec by a version and bill per route or per gateway, model the cost before committing.
The kubernetes-sigs/gateway-api repository publishes per-implementation conformance reports; read the one for your candidate at the version you will actually deploy.
How do you plan a zero-downtime migration?
Run both stacks side by side. Keep the existing Ingress controller serving production, deploy the Gateway controller with its own Service and load-balancer IP, mirror routes host by host, validate each with synthetic checks, then cut DNS per hostname with a short TTL. Avoid a big-bang cutover of every hostname at once.
The sequence that has worked for us:
- ▸Inventory. Export every Ingress object and every annotation across all namespaces; group hostnames by risk.
- ▸Bootstrap. Run the ingress2gateway CLI (a kubernetes-sigs project) to generate draft Gateways and HTTPRoutes; treat its output as a starting point, not a finished artifact.
- ▸Deploy in parallel. New controller, new IP, same certificates. Point a test hostname at it first.
- ▸Mirror and verify. Recreate one production hostname, then diff behavior: status codes, redirect chains, header handling, p99 latency.
- ▸Cut over per hostname. Lower DNS TTL to 60 seconds ahead of time; move records one by one. Rollback is a DNS revert, which is why you never reuse the old IP.
- ▸Decommission only after the old controller has served no traffic for a full business cycle, including month-end jobs.
If DNS automation runs through ExternalDNS, confirm your version reconciles Gateway resources (it supports HTTPRoute sources) so records follow the new stack automatically. For context on how the load-balancer layer in front of this behaves, see Load Balancing Beyond Round Robin: L4, L7, and Anycast.
How do you translate Ingress annotations to Gateway API fields?
Inventory annotations first, a one-line kubectl query listing every annotation key across all Ingresses tells you the real scope. Rewrites, redirects, header manipulation, and canary weights become HTTPRoute filters; timeouts are first-class HTTPRoute fields; what remains (body-size limits, rate limiting, auth) moves to controller-specific policy attachments.
The common mappings:
- ▸rewrite-target becomes a URLRewrite filter with ReplacePrefixMatch, no more capture-group regex in an annotation.
- ▸ssl-redirect becomes a RequestRedirect filter with scheme https on the HTTP listener.
- ▸canary and canary-weight become two backendRefs with weight fields on one rule, deterministic and portable.
- ▸proxy-read-timeout and friends become the timeouts block on an HTTPRoute rule (standard channel since the v1.2 era).
- ▸auth-url, rate limiting, body size have no spec equivalent yet; use your controller's policy CRDs and record each use as controller lock-in you accepted knowingly.
One hard edge: Ingress path regex has no standard equivalent. Gateway API defines Exact and PathPrefix matching, with regex as an implementation-specific option, audit every regex path before you assume it ports.
What gotchas bite teams mid-migration?
The recurring ones: TLS passthrough needs TLSRoute from the experimental channel; hostname intersection between listener and route silently drops traffic when misaligned; route precedence follows spec rules rather than file order; cert-manager needs its Gateway API integration enabled; and dashboards keyed to Ingress controller metrics go dark after cutover.
Expanded:
- ▸cert-manager solves HTTP-01 challenges against Gateway listeners only when its Gateway API support flag is enabled and the CRDs are present at startup, enable this before, not during, cutover.
- ▸Hostname intersection. A listener scoped to api.example.com will not serve an HTTPRoute declaring shop.example.com; the route attaches nowhere and reports it only in status conditions. Make kubectl checks of route status part of your validation.
- ▸Precedence. The spec ranks matches (exact hostname over wildcard, longest path first, then oldest route), controllers that previously honored annotation-based ordering will behave differently.
- ▸New load-balancer IPs interact with allowlists your customers maintain; notify them before DNS moves.
- ▸Metrics. Envoy-based controllers export different metric names than ingress-nginx; rebuild SLO dashboards and alerts ahead of the first cutover, not after the first incident.
How should GitOps and team ownership change with Gateway API?
Treat the persona split as an opportunity. Platform teams own GatewayClass and Gateway definitions in an infrastructure repository; application teams own HTTPRoutes alongside their service manifests; allowedRoutes and ReferenceGrant enforce the boundary at the API level instead of by convention. Both flow through the same GitOps pipelines with policy checks at admission.
Concretely: restrict Gateway creation to the platform team via RBAC, let app namespaces create only routes, and validate with admission policies, for example, requiring every HTTPRoute to declare a hostname from an approved list. Patterns for that enforcement layer are covered in Kubernetes Admission Control and Policy-as-Code in 2026, and the repository layout question is the same one addressed in GitOps at Scale: Running Argo CD Beyond the First Cluster. If you operate many clusters, standardize the Gateway layer fleet-wide first, the approach in From Pet Clusters to a Fleet: Multi-Cluster Kubernetes Without Snowflakes applies directly.
How TuniCyberLabs helps
We migrate production edges for a living: inventorying annotation debt, selecting and conformance-testing a controller, wiring cert-manager and ExternalDNS, and cutting over hostname by hostname with rollback rehearsed. Our teams in Tunisia and Cyprus run this for EU clients under NIS2-grade change control. If your ingress controller is approaching end of support, talk to our infrastructure team before the deadline plans the migration for you.
