Kubernetes Ingress vs Gateway API: When to Migrate and How
Gateway API v1.0.0 was released in October 2023 as a standalone SIG-Network project and is now the recommended way to manage external traffic. Ingress isn't going away, but it has real limitations that Gateway API was designed to fix. Here's what changed, what the migration looks like, and when it's worth doing.

Kubernetes Ingress has been the standard way to expose HTTP services since 1.1. It works, it's widely understood, and every Kubernetes cluster has an ingress controller. It also has fundamental limitations that have accumulated into a body of annotations-as-configuration workarounds that differ between every ingress controller implementation.
Gateway API is the replacement. Gateway API v1.0.0 was released in October 2023 as a standalone SIG-Network project — it has its own release cycle independent of Kubernetes. The CRDs are installed separately on any Kubernetes cluster; they are not bundled into Kubernetes itself. It has been production-ready since 2023 and is now the recommended path for new deployments. The question for existing clusters isn't whether to migrate — it's when and how.
What's Wrong with Ingress
Ingress was designed for a simple use case: route HTTP/HTTPS traffic to backend services based on hostname and path. That use case is common and the API handles it fine.
The problems emerge at the edges:
Annotations for everything beyond basics. Traffic splitting for canary deployments, header-based routing, request timeouts, retries, circuit breaking — none of these are in the Ingress spec. Controllers implement them as annotations, and the annotations differ between Nginx, Traefik, HAProxy, and cloud load balancers. A migration between ingress controllers means rewriting annotations, not just changing the controller.
No role separation. The Ingress API conflates infrastructure configuration (which load balancer class to use, TLS termination settings) with application configuration (routing rules). In a multi-team cluster, platform engineers manage the infrastructure and application teams manage their routes. Ingress has no way to express this separation.
Limited protocol support. Ingress is HTTP-only. TCP, UDP, gRPC, WebSocket, and other protocols are not in the spec. Again — annotations, or separate resources, or controller-specific workarounds.
No cross-namespace routing. An Ingress in namespace A cannot route to a Service in namespace B without controller-specific workarounds. Multi-namespace routing is a common requirement in multi-team clusters.
Gateway API Concepts
Gateway API introduces three core resources, each with a distinct owner:
GatewayClass
Defines a type of gateway (which controller implementation to use). Managed by infrastructure operators. Set once per cluster per implementation.
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: nginx
spec:
controllerName: gateway.nginx.org/nginx-gateway-controllerGateway
An instance of a load balancer — the actual listener. Defines which ports, protocols, and hostnames it accepts. Managed by platform/network teams.
1apiVersion: gateway.networking.k8s.io/v1
2kind: Gateway
3metadata:
4 name: production-gateway
5 namespace: infra
6spec:
7 gatewayClassName: nginx
8 listeners:
9 - name: http
10 protocol: HTTP
11 port: 80
12 - name: https
13 protocol: HTTPS
14 port: 443
15 tls:
16 mode: Terminate
17 certificateRefs:
18 - name: production-tls
19 namespace: infra
20 allowedRoutes:
21 namespaces:
22 from: Selector
23 selector:
24 matchLabels:
25 gateway-access: allowedThe allowedRoutes field is the role-separation mechanism. The Gateway owner controls which namespaces can attach routes. Application teams in permitted namespaces can then attach their own HTTPRoute objects without needing access to the Gateway itself.
HTTPRoute
Defines routing rules — which requests go to which backend services. Managed by application teams, lives in the application's namespace.
1apiVersion: gateway.networking.k8s.io/v1
2kind: HTTPRoute
3metadata:
4 name: api-route
5 namespace: production # Application team's namespace
6spec:
7 parentRefs:
8 - name: production-gateway
9 namespace: infra # Reference the Gateway in the infra namespace
10 hostnames:
11 - "api.example.com"
12 rules:
13 - matches:
14 - path:
15 type: PathPrefix
16 value: /v1
17 backendRefs:
18 - name: api-service
19 port: 8080This cross-namespace reference (HTTPRoute in production, Gateway in infra) is a first-class Gateway API feature — no annotations, no workarounds.
Feature Comparison
| Feature | Ingress | Gateway API |
|---|---|---|
| HTTP routing (path, host) | ✅ | ✅ |
| HTTPS/TLS termination | ✅ | ✅ |
| TCP/UDP routing | ❌ (annotations) | ✅ (TCPRoute, UDPRoute) |
| gRPC routing | ❌ | ✅ (GRPCRoute, GA in Gateway API v1.1.0) |
| Traffic splitting (canary) | ❌ (annotations) | ✅ (weight-based backendRefs) |
| Header-based routing | ❌ (annotations) | ✅ |
| Request/response modification | ❌ (annotations) | ✅ (filters) |
| Cross-namespace routing | ❌ | ✅ |
| Role separation | ❌ | ✅ (GatewayClass/Gateway/Route) |
| Timeouts and retries | ❌ (annotations) | ✅ (timeouts in HTTPRoute) |
| Portable across controllers | ❌ | ✅ (spec is standardised) |
Traffic Splitting (Canary Without Annotations)
One of the most immediate benefits of Gateway API: canary deployments in the spec, not in annotations.
1apiVersion: gateway.networking.k8s.io/v1
2kind: HTTPRoute
3metadata:
4 name: api-canary
5 namespace: production
6spec:
7 parentRefs:
8 - name: production-gateway
9 namespace: infra
10 hostnames:
11 - "api.example.com"
12 rules:
13 - matches:
14 - path:
15 type: PathPrefix
16 value: /
17 backendRefs:
18 - name: api-stable
19 port: 8080
20 weight: 90
21 - name: api-canary
22 port: 8080
23 weight: 1010% of traffic goes to api-canary, 90% to api-stable. To promote the canary, update weight: 100 on api-canary and weight: 0 on api-stable. To roll back, reverse it. No annotation syntax to remember, no controller-specific behaviour to learn.
Header-Based Routing
Route traffic to different backends based on request headers — useful for internal testing, feature flags, and A/B testing:
1rules:
2 - matches:
3 - headers:
4 - name: X-Feature-Flag
5 value: new-checkout
6 backendRefs:
7 - name: checkout-v2
8 port: 8080
9 - matches:
10 - path:
11 type: PathPrefix
12 value: /checkout
13 backendRefs:
14 - name: checkout-v1
15 port: 8080Requests with X-Feature-Flag: new-checkout go to checkout-v2. All other /checkout traffic goes to checkout-v1. This is controller-agnostic — the same HTTPRoute works on Nginx, Envoy, or any conformant implementation.
Request Modification (Filters)
HTTPRoute supports built-in filters for request and response modification without annotations:
1rules:
2 - matches:
3 - path:
4 type: PathPrefix
5 value: /api/v1
6 filters:
7 - type: RequestHeaderModifier
8 requestHeaderModifier:
9 add:
10 - name: X-Request-Source
11 value: gateway
12 remove:
13 - X-Internal-Token
14 - type: URLRewrite
15 urlRewrite:
16 path:
17 type: ReplacePrefixMatch
18 replacePrefixMatch: /v1
19 backendRefs:
20 - name: api-service
21 port: 8080This strips /api from the path (rewrites /api/v1/users to /v1/users), adds a header, and removes a sensitive header — all in the standard spec.
Supported Controllers
Gateway API is a spec; implementations are separate. Major implementations:
| Controller | Notes |
|---|---|
| Nginx Gateway Fabric | F5/Nginx's official Gateway API implementation — separate from the legacy ingress controller |
| Envoy Gateway | CNCF project, Envoy-based, strong Gateway API conformance |
| Istio | Full Gateway API support in Istio 1.16+; replaces Istio's own Gateway CRDs |
| Traefik (v3+) | Gateway API support as experimental → stable in v3 |
| Cilium | Gateway API support built into Cilium 1.13+ |
| AWS Load Balancer Controller | Supports Gateway API for ALB provisioning |
| GKE | Native Gateway API via GKE Gateway controller (Envoy-based) |
| AKS | Application Gateway for Containers supports Gateway API |
Check conformance scores at gateway-api.sigs.k8s.io/implementations — not all controllers implement all features.
Migration from Ingress
Step 1: Inventory Your Ingress Objects
kubectl get ingress -A -o yaml > ingress-export.yamlFor each Ingress, note:
- Hostname and path rules
- TLS configuration
- Annotations (these need to be translated to Gateway API features or filters)
- Which ingress class (
kubernetes.io/ingress.class)
Step 2: Install a Gateway API Controller
If your existing ingress controller supports Gateway API (Nginx Gateway Fabric, Traefik v3, Cilium, Istio), you can use the same controller. Otherwise, install one:
# Nginx Gateway Fabric
helm install nginx-gateway oci://ghcr.io/nginx/charts/nginx-gateway-fabric \
--namespace nginx-gateway \
--create-namespaceInstall the Gateway API CRDs — they are NOT bundled with any Kubernetes release and must be installed separately:
# Gateway API CRDs are NOT included in Kubernetes itself.
# Install them explicitly, or rely on your cloud provider's pre-installed version (GKE, AKS do this).
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yamlStep 3: Create GatewayClass and Gateway
1apiVersion: gateway.networking.k8s.io/v1
2kind: GatewayClass
3metadata:
4 name: nginx
5spec:
6 controllerName: gateway.nginx.org/nginx-gateway-controller
7---
8apiVersion: gateway.networking.k8s.io/v1
9kind: Gateway
10metadata:
11 name: production-gateway
12 namespace: infra
13spec:
14 gatewayClassName: nginx
15 listeners:
16 - name: https
17 protocol: HTTPS
18 port: 443
19 tls:
20 mode: Terminate
21 certificateRefs:
22 - name: production-tls
23 allowedRoutes:
24 namespaces:
25 from: All # Loosen during migration, tighten afterStep 4: Translate Ingress to HTTPRoute
A simple Ingress:
1# Before: Ingress
2apiVersion: networking.k8s.io/v1
3kind: Ingress
4metadata:
5 name: api-ingress
6 namespace: production
7 annotations:
8 nginx.ingress.kubernetes.io/rewrite-target: /
9spec:
10 ingressClassName: nginx
11 rules:
12 - host: api.example.com
13 http:
14 paths:
15 - path: /api
16 pathType: Prefix
17 backend:
18 service:
19 name: api-service
20 port:
21 number: 80801# After: HTTPRoute
2apiVersion: gateway.networking.k8s.io/v1
3kind: HTTPRoute
4metadata:
5 name: api-route
6 namespace: production
7spec:
8 parentRefs:
9 - name: production-gateway
10 namespace: infra
11 hostnames:
12 - "api.example.com"
13 rules:
14 - matches:
15 - path:
16 type: PathPrefix
17 value: /api
18 filters:
19 - type: URLRewrite
20 urlRewrite:
21 path:
22 type: ReplacePrefixMatch
23 replacePrefixMatch: /
24 backendRefs:
25 - name: api-service
26 port: 8080Step 5: Run Both in Parallel
Keep the Ingress running while testing the HTTPRoute. Point a test subdomain to the new Gateway. Verify traffic flows correctly. Once validated, update DNS to point the production hostname to the Gateway's load balancer IP, and delete the Ingress.
When Not to Migrate Yet
Your controller doesn't have stable Gateway API support. Check conformance for your specific controller version. Partial conformance means some features behave differently than the spec describes.
You have complex annotations that don't map cleanly. Some controller-specific annotations have no direct Gateway API equivalent. If your Ingress relies heavily on Nginx-specific rate limiting or auth annotations, audit the equivalent mechanisms first.
Your team is mid-sprint. This is a non-trivial migration on complex clusters. Plan a maintenance window and rollback path.
Frequently Asked Questions
Is Ingress deprecated?
Not officially removed or deprecated in any current Kubernetes version. The Kubernetes SIG-Network has stated that Ingress will be maintained for the foreseeable future. But Gateway API is the direction of development — new features will go into Gateway API, not Ingress.
Do I need to run two ingress controllers?
Not necessarily. If your ingress controller supports Gateway API (Nginx Gateway Fabric, Traefik v3, Cilium), you can run Gateway API and Ingress resources simultaneously using the same controller, migrating routes incrementally.
Does Gateway API work with cert-manager?
Yes. cert-manager 1.14+ supports Gateway resources as certificate request targets via the gateway.networking.k8s.io/v1.Gateway reference. The annotation-based approach for Ingress still works too, so you can migrate TLS separately from routing.
What about service meshes?
Istio, Linkerd, and Cilium all support Gateway API for north-south traffic (external to cluster). For east-west traffic (pod-to-pod within the cluster), service meshes use their own APIs (or the experimental GAMMA initiative in Gateway API). For now, Gateway API is primarily relevant for ingress — not as a replacement for service mesh east-west policies.
For network-level security on top of routing, see Kubernetes Network Policies: A Practical Guide. For production ingress-nginx configuration including TLS, ModSecurity WAF, and rate limiting, see Kubernetes Ingress-NGINX in Production. For AWS ALB integration via the Load Balancer Controller, see AWS Load Balancer Controller on EKS. For an in-depth guide to Gateway API resources — HTTPRoute, GRPCRoute, ReferenceGrant, traffic splitting, and AWS ALB integration — see Kubernetes Gateway API: The Modern Replacement for Ingress.
Migrating from Ingress to Gateway API on a production cluster? Talk to us at Coding Protocols — we help platform teams manage traffic infrastructure migrations with zero downtime.


