The Ingress API served Kubernetes well for years, but its limitations — reliance on unportable annotations, no standard for traffic splitting, header matching, or delegation — led the community to build its successor: the Gateway API. It's now the officially recommended way to handle ingress traffic in Kubernetes.
ingress-nginx controller project has announced deprecation, with the NGINX Gateway Fabric (implementing Gateway API) as its replacement. New projects should use Gateway API. Existing Ingress setups continue to work but won't receive new features.
1. Why Gateway API Replaces Ingress
| Ingress Limitation | Gateway API Solution |
|---|---|
| Features via annotations (not portable) | First-class typed fields in the spec |
| No role separation (one resource, one owner) | Layered resources: infra team owns Gateway, app teams own Routes |
| HTTP only | HTTP, gRPC, TCP, UDP, TLS passthrough — all supported |
| No traffic splitting standard | Built-in weighted backends for canary/blue-green |
| No header/query matching | Rich matching: headers, query params, methods |
| No request/response manipulation | Built-in filters: rewrite, redirect, add headers, mirror |
| Single implementation per Ingress | Multiple Gateways, different implementations in same cluster |
The Resource Model
allowedRoutes.
2. GatewayClass & Gateway
GatewayClass — "What implementation?"
# Installed by the controller vendor (like IngressClass): apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: nginx # or: cilium, envoy, istio, etc. spec: controllerName: gateway.nginx.org/nginx-gateway-controller
Common GatewayClass controllers:
| Implementation | controllerName | Notes |
|---|---|---|
| NGINX Gateway Fabric | gateway.nginx.org/nginx-gateway-controller | Official successor to ingress-nginx |
| Cilium | io.cilium/gateway-controller | eBPF-based, high performance |
| Istio | istio.io/gateway-controller | Service mesh integration |
| Envoy Gateway | gateway.envoyproxy.io/gatewayclass-controller | Standalone Envoy for Gateway API |
| Traefik | traefik.io/gateway-controller | Traefik v3+ |
| AWS Gateway API Controller | application-networking.k8s.aws/gateway-api-controller | Maps to AWS VPC Lattice |
| GKE | networking.gke.io/gateway | Native GCP load balancers |
Gateway — "Where to listen?"
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
namespace: infra
spec:
gatewayClassName: nginx # Reference to GatewayClass
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All # Any namespace can attach routes
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls # TLS Secret
namespace: infra
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: "true" # Only labeled namespaces can use HTTPS
allowedRoutes — The Guardrail
| Setting | Meaning |
|---|---|
from: All | Any namespace can attach Routes to this listener |
from: Same | Only Routes in the same namespace as the Gateway |
from: Selector | Only namespaces matching the label selector |
allowedRoutes field is how platform teams implement multi-tenancy for ingress. The Gateway is in a shared infra namespace; app teams attach HTTPRoutes from their own namespaces. The Gateway controls which namespaces are allowed — preventing unauthorized teams from hijacking traffic.
3. HTTPRoute — "How to route?"
Basic Routing
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-routes
namespace: app-team # App team's namespace
spec:
parentRefs:
- name: production-gateway # Attach to which Gateway
namespace: infra
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-svc
port: 80
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-svc
port: 80
Rich Matching (Beyond Ingress Capabilities)
rules:
- matches:
- path:
type: PathPrefix
value: /api
headers: # ← Match by HTTP header
- name: X-Version
value: "beta"
method: GET # ← Match by HTTP method
queryParams: # ← Match by query parameter
- name: debug
value: "true"
backendRefs:
- name: api-beta-svc
port: 80
Traffic Splitting (Canary/Blue-Green)
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-v1 # 90% of traffic
port: 80
weight: 90
- name: web-v2 # 10% canary
port: 80
weight: 10
nginx.ingress.kubernetes.io/canary-weight: "10") that only some controllers supported. In Gateway API, weighted backends are standard — every conformant implementation must support them.
Filters — Request/Response Manipulation
rules:
- matches:
- path:
type: PathPrefix
value: /old-path
filters:
- type: RequestRedirect # 301/302 redirect
requestRedirect:
scheme: https
hostname: new.example.com
statusCode: 301
- matches:
- path:
type: PathPrefix
value: /api/v1
filters:
- type: URLRewrite # Rewrite before forwarding
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /v1
- type: RequestHeaderModifier # Add/set/remove headers
requestHeaderModifier:
add:
- name: X-Forwarded-By
value: gateway
remove:
- X-Internal-Only
backendRefs:
- name: api-svc
port: 80
Available Filter Types
| Filter | Purpose | Example |
|---|---|---|
RequestHeaderModifier | Add/set/remove request headers | Add auth headers, strip internal headers |
ResponseHeaderModifier | Add/set/remove response headers | Add CORS headers, security headers |
RequestRedirect | HTTP redirect (301/302) | HTTP→HTTPS, domain migration |
URLRewrite | Rewrite path/host before forwarding | Strip prefix, change hostname |
RequestMirror | Copy traffic to another backend (no response) | Shadow testing new versions |
ExtensionRef | Controller-specific custom filters | Rate limiting, auth (via CRDs) |
4. Beyond HTTP — Other Route Types
| Route Type | Protocol | Use Case | Status |
|---|---|---|---|
HTTPRoute | HTTP/HTTPS | Web apps, APIs | GA (v1) |
GRPCRoute | gRPC | gRPC services with method-level routing | GA (v1) |
TLSRoute | TLS (passthrough) | Pass encrypted traffic without termination | Experimental |
TCPRoute | Raw TCP | Databases, Redis, custom protocols | Experimental |
UDPRoute | Raw UDP | DNS, game servers | Experimental |
# GRPCRoute example — route by gRPC service/method:
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: grpc-routes
spec:
parentRefs:
- name: production-gateway
rules:
- matches:
- method:
service: myapp.UserService
method: GetUser
backendRefs:
- name: user-svc
port: 50051
5. Migrating from Ingress to Gateway API
Comparison: Same routing, different specs
# OLD — Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
# NEW — Gateway API:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web
spec:
parentRefs:
- name: production-gateway
hostnames: ["app.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: api-svc
port: 80
Migration Strategy
- Install a Gateway API controller (e.g., NGINX Gateway Fabric, Cilium, Envoy Gateway)
- Create GatewayClass + Gateway (platform team)
- Run both in parallel — Ingress and Gateway API can coexist
- Migrate routes one by one — convert Ingress → HTTPRoute
- Validate traffic — confirm identical behavior
- Remove old Ingress resources and controller
ingress2gateway (from the Gateway API project) can auto-convert Ingress resources to HTTPRoutes. It handles common patterns but may need manual adjustment for complex annotations. Run both systems in parallel during migration — don't big-bang switch.
6. NGINX Gateway Fabric — The Official Successor
NGINX Gateway Fabric is the official replacement for ingress-nginx. It implements the Gateway API spec using NGINX as the data plane.
# Installation: kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/latest/download/crds.yaml kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/latest/download/nginx-gateway.yaml # Creates GatewayClass "nginx" automatically
| Feature | ingress-nginx (deprecated) | NGINX Gateway Fabric |
|---|---|---|
| API | Ingress + annotations | Gateway API (typed resources) |
| Traffic splitting | Canary annotation (limited) | Native weighted backends |
| Header matching | Custom snippets (unsafe) | First-class matches.headers |
| URL rewrite | Annotation + regex | Typed URLRewrite filter |
| Role separation | None (one Ingress per route) | Gateway (infra) + HTTPRoute (app) |
| Multi-tenancy | Weak (namespace annotations) | Strong (allowedRoutes with selectors) |
Summary
| Concept | Key Point |
|---|---|
| Gateway API | Official successor to Ingress — richer, portable, role-based |
| GatewayClass | "What controller?" — installed by vendor (like IngressClass) |
| Gateway | "Where to listen?" — ports, TLS, allowed namespaces (platform team) |
| HTTPRoute | "How to route?" — path, headers, methods, backends (app team) |
| Traffic splitting | Weighted backendRefs — built-in canary/blue-green |
| Filters | Redirect, rewrite, header modification, mirror — no annotations |
| Role separation | Infra owns Gateway, apps own Routes — self-service with guardrails |
| NGINX Gateway Fabric | Official replacement for ingress-nginx |
| Migration | Run in parallel, use ingress2gateway tool, migrate incrementally |
📝 Quiz: Gateway API
Q1: Who creates the Gateway and who creates the HTTPRoute in a typical multi-team setup?
parentRefs. This separation means app teams don't need cluster-admin access to manage routing.Q2: You want to send 5% of traffic to a canary deployment. How do you do this in Gateway API?
backendRefs in the HTTPRoute:backendRefs:
- name: web-stable
port: 80
weight: 95
- name: web-canary
port: 80
weight: 5No annotations needed — this is a standard, portable feature that all conformant implementations must support.Q3: How does a Gateway control which namespaces can attach HTTPRoutes to it?
listeners[].allowedRoutes.namespaces. Options:•
from: All — any namespace•
from: Same — only the Gateway's namespace•
from: Selector — only namespaces with matching labelsThis prevents unauthorized teams from routing traffic through the Gateway.
Q4: What can Gateway API HTTPRoute do that Ingress cannot (without annotations)?
• Header matching
• Query parameter matching
• HTTP method matching
• Weighted traffic splitting
• URL rewriting
• Request/response header modification
• Traffic mirroring
• Redirects
Q5: Can Ingress and Gateway API coexist in the same cluster during migration?
Q6: What replaces ingress-nginx for new projects?