- Published on
From Ingress to Gateway API — Why, What, and How It Changes
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- Introduction: Why Gateway API Now
- The Limits of Ingress — Why It Must Change
- The Gateway API Resource Model — What Changes
- Side-by-Side YAML Comparison
- Migration Strategy — How to Change It
- Per-Controller Gateway API Support Status
- Pitfalls and Troubleshooting
- Closing
- References
Introduction: Why Gateway API Now
For the past several years, the de facto standard for exposing L7 traffic externally in Kubernetes was Ingress. But in 2026 the situation has clearly changed. The Ingress API is frozen — no new features are being added — and Gateway API is establishing itself as the successor standard. At the same time, the once-ubiquitous ingress-nginx is trending toward maintenance mode, operating with a focus on security patches rather than new features.
This article is organized along three axes: "Why it must change," "What changes," and "How to change it." Rather than merely introducing a new API, it focuses on the realistic order in which a team with existing Ingress assets can migrate.
The Limits of Ingress — Why It Must Change
Ingress is excellent for simple routing, but two fundamental limits surface as operational scale grows.
1. Annotation Hell
The standard Ingress spec (rules/paths/backend/tls) covers only basic host/path routing. Yet in practice you need many advanced features: header-based routing, weighted traffic splitting (canary), timeouts, retries, CORS, auth, rewrites, and so on. Since the Ingress standard lacks these, each controller worked around them with annotations.
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Custom: value";
The problem is that these annotations are completely different per controller. ingress-nginx's nginx.ingress.kubernetes.io/canary-weight does not work in Traefik, and Traefik's middleware annotations are meaningless in ingress-nginx. As a result, an Ingress resource becomes strongly coupled to a specific controller, and switching controllers means rewriting every annotation. This is the portability problem known as "annotation hell."
Moreover, free-text annotations like configuration-snippet can inject arbitrary nginx config, which is also a security risk; related vulnerabilities have in fact been reported.
2. No Role Separation
A single Ingress resource mixes infrastructure concerns (TLS certificates, entry-point config) with application concerns (which path goes to which service). In a large organization, the platform team wants to manage the entry point, certificates, and policy, while each application team wants to manage only its own service's routing. But Ingress has no standard mechanism to separate these responsibilities. Every team either touches the same Ingress resource or splits it awkwardly per namespace.
The Gateway API Resource Model — What Changes
Gateway API introduces several role-separated resources to tackle both problems head-on. The core resources are as follows.
- GatewayClass: A cluster-scoped resource defining which implementation (controller) to use. It corresponds to Ingress's IngressClass and is usually created by the infrastructure provider.
- Gateway: Defines the actual entry point (listeners, ports, protocols, TLS). Owned by the platform/infrastructure team.
- HTTPRoute: Defines HTTP routing rules (hostnames, paths, header matching, traffic splitting, filters). Owned by application developers.
- TLSRoute / TCPRoute / GRPCRoute / UDPRoute: Route resources for non-HTTP protocols.
- ReferenceGrant: A resource that explicitly permits cross-namespace references.
Thanks to this separation, the infra team manages Gateways and the app teams manage HTTPRoutes, while sharing a single entry point.
Concept Mapping Table
Here is where existing Ingress concepts map in Gateway API.
| Ingress concept | Gateway API equivalent | Notes |
|---|---|---|
| IngressClass | GatewayClass | Cluster-scoped, implementation selection |
| Ingress (entry-point part) | Gateway + listener | Defines port/protocol/TLS |
| Ingress (routing-rule part) | HTTPRoute | Host/path/match rules |
| spec.rules.host | HTTPRoute hostnames | Host matching |
| spec.rules.http.paths | HTTPRoute rules.matches | Path/header matching |
| pathType Prefix/Exact | PathPrefix/Exact match | Almost identical meaning |
| backend.service | backendRefs | Service reference |
| spec.tls | Gateway listener TLS | Terminated at entry point |
| canary annotation | backendRefs weight | Standard traffic splitting |
| rewrite annotation | URLRewrite filter | Standard filter |
| header-manipulation annotation | RequestHeaderModifier filter | Standard filter |
The key point is that many features previously worked around with annotations become standard spec fields in Gateway API. Traffic splitting, rewrites, header manipulation, redirects — all are expressed in a portable, standard form.
Side-by-Side YAML Comparison
Let us express the same requirements in Ingress and Gateway API to see the difference.
Basic Host/Path Routing
In Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
In Gateway API, it splits into a Gateway and an HTTPRoute.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: shop-gateway
spec:
gatewayClassName: envoy
listeners:
- name: http
protocol: HTTP
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: shop-route
spec:
parentRefs:
- name: shop-gateway
hostnames:
- shop.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
Weighted Traffic Splitting (Canary)
In Ingress (ingress-nginx), a separate canary Ingress and annotations were required.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: shop-v2
port:
number: 80
In Gateway API, it is standardized as backendRefs weights and expressed in a single resource.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: shop-canary
spec:
parentRefs:
- name: shop-gateway
hostnames:
- shop.example.com
rules:
- backendRefs:
- name: shop-v1
port: 80
weight: 80
- name: shop-v2
port: 80
weight: 20
TLS and Header Filters
An example terminating TLS at the Gateway and adding a header in the HTTPRoute.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: secure-gateway
spec:
gatewayClassName: envoy
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: shop-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: secure-route
spec:
parentRefs:
- name: secure-gateway
hostnames:
- shop.example.com
rules:
- filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Env
value: prod
backendRefs:
- name: shop-service
port: 80
Migration Strategy — How to Change It
Do not try to finish the migration in one shot; proceed in stages.
The ingress2gateway Tool
Kubernetes SIG Network provides ingress2gateway, a tool that converts existing Ingress (and some provider CRDs) into Gateway API resources. It converts standard Ingress fields into HTTPRoute/Gateway, and maps some provider-specific annotations where possible.
ingress2gateway print --input-file=ingress.yaml
This tool gives you a starting point only; it cannot auto-convert every annotation. Non-standardized controller-specific features (free-text snippets, etc.) require manual review. So always review the conversion output and validate behavior in a reproducible environment.
Gradual Transition
The recommended order is as follows.
- Prepare: Additionally install a controller that supports Gateway API in the cluster. Leave the existing Ingress controller in place.
- Run in parallel: Expose new services or canary traffic via Gateway/HTTPRoute first. Shift traffic gradually with DNS or weights.
- Convert: Bulk-convert existing Ingress with ingress2gateway, then review and adjust the result.
- Validate: Confirm in staging that routing, TLS, headers, and timeout behavior are identical.
- Cut over: After fully moving traffic to the Gateway, remove the old Ingress.
During this process, be careful that the two systems do not expose the same host simultaneously.
Per-Controller Gateway API Support Status
Gateway API is a standard spec, and several controllers implement it. The conformance level (degree of Core / Extended feature support) differs per controller.
| Implementation | Data plane | Notes |
|---|---|---|
| Envoy Gateway | Envoy | CNCF, designed around Gateway API |
| Contour | Envoy | Supports both HTTPProxy and Gateway API |
| Istio | Envoy | Service mesh, mature Gateway API support |
| Traefik | Traefik | Supports Ingress, CRDs, and Gateway API |
| Cilium | eBPF/Envoy | CNI-integrated Gateway support |
| Kong | Kong | Gateway API support |
| NGINX Gateway Fabric | NGINX | NGINX-based Gateway API implementation |
| HAProxy | HAProxy | Gateway API support in progress |
When actually selecting, it is safest to check the official conformance reports to verify that the features you need (traffic splitting, header matching, etc.) are supported.
Pitfalls and Troubleshooting
Problems frequently encountered during the transition.
1. An HTTPRoute does not attach to the Gateway.
Either parentRefs is wrong, or the Gateway listener's allowedRoutes (namespace/kind restrictions) blocks it. Check the attach condition in the HTTPRoute status.
kubectl get httproute shop-route -o yaml | grep -A20 status
2. Cross-namespace references are denied. When the Gateway and HTTPRoute, or the HTTPRoute and backend Service, live in different namespaces, a ReferenceGrant is required. The reference only holds once this is explicitly permitted.
3. After conversion, behavior is subtly different. Annotation-based features (especially rewrites, regex paths, snippets) may not map exactly 1:1. First check whether they can be expressed with standard filters; if not, evaluate implementation-specific extensions.
4. The TLS certificate is not applied.
If the Secret referenced by the Gateway listener's certificateRefs is in a different namespace, a ReferenceGrant is required. cert-manager supports Gateway API integration, so you can wire certificate issuance directly to the Gateway resource.
5. Two controllers claim the same host. During parallel operation, if the old Ingress and the new Gateway expose the same host, traffic gets mixed. Separate them clearly by host or weight until cutover.
Closing
Migrating to Gateway API is not a mere API swap but an evolution of the operating model. Understanding Why (annotation hell and the lack of role separation), What (responsibility separation into GatewayClass/Gateway/HTTPRoute and standard filters), and How (ingress2gateway and gradual parallel transition) in order lets you design the migration smoothly.
In the 2026 context, Ingress still works but is frozen, and ingress-nginx is maintenance-focused. So it is reasonable to put Gateway API first for new designs and move existing assets in stages after validation. The conversion tool is only a starting point and does not replace every annotation, so the key is always validation.
References
- Gateway API official site: https://gateway-api.sigs.k8s.io/
- Gateway API — Migrating from Ingress: https://gateway-api.sigs.k8s.io/guides/migrating-from-ingress/
- ingress2gateway project: https://github.com/kubernetes-sigs/ingress2gateway
- Kubernetes official docs — Ingress: https://kubernetes.io/docs/concepts/services-networking/ingress/
- Envoy Gateway official docs: https://gateway.envoyproxy.io/docs/
- Project Contour official docs: https://projectcontour.io/docs/
- Istio Gateway API docs: https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/
- Traefik Kubernetes Gateway API docs: https://doc.traefik.io/traefik/providers/kubernetes-gateway/
- cert-manager — using Gateway API: https://cert-manager.io/docs/usage/gateway/
- ingress-nginx official docs: https://kubernetes.github.io/ingress-nginx/