Gateway API vs Ingress: When Is Migration Worth It?
Gateway API addresses Ingress limits around roles, annotations, and traffic routing, but migration still requires conversion and implementation testing.
TL;DR
Ingress remains a sensible fit for straightforward HTTP/HTTPS routing and TLS termination, but its annotation model and flat ownership assumptions become expensive in multi-team clusters. Gateway API separates infrastructure and route ownership through GatewayClass, Gateway, HTTPRoute, listeners, parentRefs, and allowedRoutes. It also makes capabilities such as header matching and traffic weighting part of the route model rather than controller-specific annotations. The trade-off is real migration work: default backends need an explicit design, ingress2gateway is only a starting point, and implementation-specific behavior still needs testing. The four personas are valuable only when ownership is genuinely split; the pros and cons therefore favor migration when the cluster already pays for annotation lock-in or unclear ownership, not merely because Gateway API is newer. Netics’ position is to treat the migration as a measured platform decision.
What Ingress actually is, and where it runs out of room

The Ingress API is the standard Kubernetes mechanism for external HTTP and HTTPS load balancing in front of Services. It handles TLS termination and simple content-based routing of HTTP traffic through a single resource kind, and it has broad ecosystem support — cert-manager and ExternalDNS both integrate with it directly. For a related Netics view on production architecture, see Netics infrastructure decision analysis.
Its limitations are structural, not incidental. The Kubernetes Gateway API project's migration guidance names three of them plainly: Ingress supports only TLS termination and basic content-based routing; anything past that goes through annotations, which are implementation-specific and don't port between controllers; and Ingress has no real permission model for clusters where multiple teams share load-balancing infrastructure. Those three constraints — routing surface, annotation lock-in, and a flat permission model — are the entire case for Gateway API, not a subset of it.
Gateway API's resource model: GatewayClass, Gateway, HTTPRoute
Gateway API replaces the single Ingress object with three interdependent stable kinds (a fourth, GRPCRoute, covers gRPC-specific routing and isn't discussed further here):
- GatewayClass defines a set of gateways sharing common configuration, managed by a controller that implements the class. A minimal example just names a controller: controllerName: example.com/gateway-controller.
- Gateway defines an instance of traffic-handling infrastructure — a cloud load balancer, for example — and references exactly one GatewayClass.
- HTTPRoute defines HTTP-specific rules that map traffic from a Gateway's listener to backend Services.
The relationship is deliberately layered: a Gateway points at one GatewayClass, and one or more routes (HTTPRoute being the common case) attach to a Gateway. A Gateway can restrict which routes may attach to its listeners, which is what makes shared, multi-team load-balancing infrastructure workable in the first place.
Four personas instead of one
This is the design principle Kubernetes' own docs lead with, and it's the part of the pitch that's easy to skim past: Gateway API is role-oriented. The original Ingress API had a single resource and a single persona — the user who owns the Ingress object. Gateway API models three organizational roles directly into the API: the Infrastructure Provider (manages the infrastructure serving multiple isolated clusters or tenants — a cloud provider, typically), the Cluster Operator (manages clusters, policies, network access, application permissions), and the Application Developer (manages an application's own configuration and Service composition). Kubernetes' migration guide describes the Ingress model's single persona as a named limitation, not a footnote — it's the reason Ingress "is not well-suited for multi-team clusters with shared load-balancing infrastructure."
Whether this matters to a given cluster depends entirely on whether those roles are actually different people today. If one team owns the whole stack from load balancer to application code, four personas is API surface with no organizational referent underneath it.
Listeners, parentRefs, and allowedRoutes: how the pieces attach

A Gateway exposes one or more listeners — the network endpoints (hostname, port, protocol) it will accept traffic on. An HTTPRoute attaches to a Gateway's listener through a parentRefs field, which names the Gateway (and optionally the specific listener) the route wants to bind to.
The attachment isn't automatic just because a route asks for it. A Gateway can declare allowedRoutes on a listener — restricting which routes, by namespace or by kind, are permitted to attach. This is the mechanism that makes the role separation from the previous section operational rather than aspirational: an Infrastructure Provider or Cluster Operator can own a Gateway and its listeners, while explicitly deciding which Application Developer teams' HTTPRoutes are allowed to bind to it. Ingress has no equivalent gate — any Ingress resource that targets a given IngressClass just applies.
Header matching and traffic weighting: native versus annotation-only
Two capabilities that Gateway API supports as first-class HTTPRoute fields are commonly bolted onto Ingress through controller-specific annotations instead: matching requests by header value, and splitting traffic by weight across backends (canary releases, blue-green cutovers). On Ingress, both require reaching for an annotation whose syntax and behavior are specific to whichever controller is deployed — NGINX Ingress, Traefik, and cloud-managed controllers each implement this differently, and none of that config is portable if the controller changes. On Gateway API, header matching and weighting are part of the HTTPRoute spec itself, which is the concrete form the "annotations aren't portable" limitation takes in practice.
The default backend gap
Ingress supports a default backend: a fallback destination for requests that don't match any rule. Gateway API's HTTPRoute model doesn't carry an equivalent implicit fallback — the closest pattern is an explicit catch-all route with a low-priority match. Teams relying on a default backend for unmatched-request handling need to design that fallback explicitly rather than assume it carries over.
What ingress2gateway does and doesn't do
ingress2gateway is the official conversion tool referenced directly in the Kubernetes Gateway API migration guide, and it automates part of translating existing Ingress resources into Gateway API equivalents. It's explicitly scoped, though: the migration guide's own framing is that it "will not prepare you for a live migration or explain how to convert some implementation-specific features of your Ingress controller." Vendor annotations — the exact things Gateway API exists to get away from — are, by definition, outside what a generic converter can map, since they have no standard equivalent for the tool to target. Practically, that means the tool gets a cluster from zero to a rough draft, not from Ingress to a validated, production-ready Gateway API config in one pass.
A hypothetical migration, worked through
Take a concrete case — this is illustrative, not a real Netics deployment. Imagine a mid-sized SaaS platform running one Ingress resource per service, about forty in total, spread across five product teams sharing a single NGINX Ingress controller. Canary releases go through an NGINX-specific canary-weight annotation; a security team wants to own TLS and edge routing centrally while product teams keep owning their own request-matching rules; and onboarding a new team currently means walking them through NGINX's annotation dialect from scratch, because nothing about Ingress separates "who owns the edge" from "who owns the route."
Under Gateway API, that split maps directly onto the persona model: the security team becomes the Cluster Operator, owns the Gateway and its listeners, and uses allowedRoutes to scope which namespaces' HTTPRoutes can attach. Each product team owns its own HTTPRoutes, including native weighted backends for canaries, with no controller-specific annotation to learn. ingress2gateway converts the standard-routing majority of those forty Ingress resources as a starting point; the NGINX canary annotations and any other controller-specific behavior still need to be re-implemented by hand as HTTPRoute weight rules, and every converted route needs validation against real traffic before cutover.
Pros and cons
Gateway API, in favor:
- Native header matching and traffic weighting, no vendor annotation dialect
- Role separation (allowedRoutes, persona model) fits multi-team clusters with a real ownership split
- Portable core resources — GatewayClass, Gateway, HTTPRoute — not tied to one controller's annotation syntax
Gateway API, against:
- No default-backend equivalent; fallback routing must be designed explicitly
- ingress2gateway converts the standard case, not implementation-specific annotations — that part is manual
- Extra resource kinds and a persona model to actually staff, not just configure, if the roles don't already exist as separate teams
Ingress, still valid when:
- Routing needs are simple (host/path rules, TLS termination) and likely to stay that way
- One team owns the whole stack end to end — the persona split maps to nobody
- The existing Ingress controller and its annotations are stable, documented internally, and not actively causing incidents
A migration checklist
- Inventory every annotation currently in use across all Ingress resources, per controller — this is the part ingress2gateway won't do for you.
- Confirm whether the four Gateway API personas map to actual distinct teams today. If not, the role separation is overhead, not a fix.
- Run ingress2gateway against existing manifests to get a converted baseline, then diff it against what production actually does.
- Explicitly design a catch-all HTTPRoute for any traffic currently relying on an Ingress default backend.
- Rebuild controller-specific behavior (canary weighting, custom header rules) as native HTTPRoute fields rather than porting the annotation syntax.
- Validate allowedRoutes scoping in a non-production Gateway before granting any team's HTTPRoutes production attachment rights.
- Cut over incrementally, route by route, with rollback to the original Ingress resource kept available until the HTTPRoute is verified under real traffic.
Netics' position
Gateway API is a better-designed answer to a set of limitations that are real and specifically named in Kubernetes' own documentation — annotation lock-in, a flat permission model, no native traffic control. That's a legitimate case for migrating. It is not, on its own, a case for migrating everything, on a deadline, because a newer spec exists. The clusters where this pays off are the ones already paying the Ingress tax today: annotation sprawl nobody can fully explain, or routing ownership spread across teams with no boundary between them. A single-team cluster running a handful of straightforward Ingress rules gains persona machinery it doesn't need and loses a default-backend behavior it has to rebuild. Read the limitations list against your own cluster before reading it as a mandate.
If your team is weighing this against a broader Kubernetes networking or platform review, Netics can walk through your specific setup.
*Sources: "Gateway API," Kubernetes documentation, Kubernetes Gateway API project — kubernetes.io / gateway-api.sigs.k8s.io, retrieved 31 August 2026.