
- Overview
- Gateway API vs Ingress: Why You Should Switch
- Architecture
- Comparing Gateway API Implementations
- The Core Resources
- Traffic Management Patterns
- Installing and Configuring Envoy Gateway
- Troubleshooting
- Failure Cases and Recovery Procedures
- Operations Checklist
- Conclusion
- References
Overview
As of March 2026, with the end of maintenance for Kubernetes Ingress NGINX imminent, moving to the Gateway API is no longer a choice but a necessity. The Gateway API has been released through v1.4, promoting core features such as BackendTLSPolicy, Named Rules and supportedFeatures to the Standard channel, and Envoy Gateway has evolved through v1.6.x to provide enterprise-grade traffic management features such as SecurityPolicy, global/local rate limiting and mTLS.
In this article, we cover how to understand the Gateway API's core resource structure and how to actually manage traffic in a production environment with Envoy Gateway as the implementation. It presents patterns you can apply right away, with code: canary deployments, traffic mirroring, header-based routing, rate limiting, and backend mTLS using BackendTLSPolicy.
Gateway API vs Ingress: Why You Should Switch
The Ingress resource has been the standard for HTTP traffic routing since the early days of Kubernetes, but it had a structural limitation: it depended on vendor-specific annotations. The Gateway API solves that problem at the root.
| Comparison | Ingress | Gateway API |
|---|---|---|
| Protocol support | HTTP/HTTPS only | HTTP, gRPC, TCP, UDP and TLS all supported |
| Routing expressiveness | Host/path based only | Matching on headers, query parameters and methods |
| Role separation | Everything in a single resource | Roles split across GatewayClass, Gateway, Route |
| Vendor lock-in | Annotation-based extension (vendor-specific) | Standardized CRD-based extension (portable) |
| Traffic splitting | Not natively supported | Weight-based traffic splitting natively supported |
| TLS management | Basic TLS termination only | Backend mTLS supported through BackendTLSPolicy |
| Multi-tenancy | Limited | Cross-namespace route sharing natively supported |
There are three core reasons to switch. First, Ingress NGINX reaches end of maintenance in March 2026, so security patches stop. Second, the Gateway API can manage L4/L7 protocols together, handling TCP, UDP and gRPC workloads without a separate mechanism. Third, the role separation aligned with 4 personas (infrastructure provider, cluster operator, application administrator, application developer) maximizes operational efficiency in a multi-team environment.
Architecture
The Gateway API Resource Hierarchy
The Gateway API is split into three layers. The GatewayClass is a template defined by the infrastructure provider. The Gateway is the load balancer instance the cluster operator creates from a GatewayClass. A Route (HTTPRoute, GRPCRoute, TLSRoute, TCPRoute) is the resource in which the application developer defines the traffic routing rules.
Thanks to this hierarchy, the cluster operator manages the TLS certificates and listener ports while application developers deploy routing rules independently within their own namespace. The ReferenceGrant resource lets you control cross-namespace reference permissions explicitly.
Envoy Gateway Architecture
Envoy Gateway is an implementation of the Gateway API, made up of a control plane and a data plane. The control plane watches Gateway API resources and converts them into Envoy proxy configuration. The data plane is the set of Envoy proxy instances that actually handle traffic. When the Envoy Gateway controller detects a Gateway resource, it automatically provisions the Envoy proxy Deployment and Service.
What sets Envoy Gateway apart is that extension CRDs such as BackendTrafficPolicy, SecurityPolicy, ClientTrafficPolicy and EnvoyExtensionPolicy let you manage rate limiting, authentication/authorization and traffic control declaratively.
Comparing Gateway API Implementations
Use the following comparison when choosing a Gateway API implementation for a production environment.
| Implementation | Data plane | Gateway API version | Key characteristics |
|---|---|---|---|
| Envoy Gateway | Envoy Proxy | v1.2+ | Native rate limiting, SecurityPolicy, AI Gateway extensions |
| Istio (Ambient) | Envoy Proxy / ztunnel | v1.1+ | Service mesh integration, no sidecar in Ambient mode |
| NGINX Gateway Fabric | NGINX | v1.2+ | NGINX based, familiar to existing NGINX users |
| Cilium Gateway | eBPF / Envoy | v1.1+ | eBPF-based L4 acceleration, network policy integration |
| Kong Gateway | Kong Proxy | v1.2+ | Plugin ecosystem, API management integration |
| Traefik | Traefik Proxy | v1.1+ | Automatic certificate management, simple configuration |
In this article, we cover Envoy Gateway in particular, but because it follows the Gateway API standard, the core resource definitions (GatewayClass, Gateway, HTTPRoute, GRPCRoute) apply identically to other implementations.
The Core Resources
GatewayClass and Gateway
The GatewayClass is a cluster-scoped resource that specifies which controller manages the Gateway. The Gateway is a namespace-scoped resource that defines the actual listeners (port, protocol, TLS settings).
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy-gateway
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
namespace: infra
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
gatewayClassName: envoy-gateway
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: wildcard-tls
namespace: infra
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: 'true'
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
In this configuration the HTTPS listener allows only Routes from namespaces carrying the gateway-access: "true" label. The HTTP listener allows only Routes from the same namespace and is used purely for the HTTPS redirect. This kind of fine-grained access control through allowedRoutes is one of the Gateway API's biggest advantages over Ingress.
HTTPRoute
HTTPRoute defines the routing rules for HTTP traffic. The Named Rules feature introduced in Gateway API v1.4 lets you give each rule a name, which strengthens observability and policy targeting.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route
namespace: app
spec:
parentRefs:
- name: production-gateway
namespace: infra
sectionName: https
hostnames:
- 'api.example.com'
rules:
- name: health-check
matches:
- path:
type: Exact
value: /healthz
backendRefs:
- name: api-service
port: 8080
- name: api-v2
matches:
- path:
type: PathPrefix
value: /api/v2
headers:
- name: X-API-Version
value: '2'
backendRefs:
- name: api-v2-service
port: 8080
- name: api-default
matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-v1-service
port: 8080
Named Rules (name: health-check, name: api-v2, name: api-default) are useful when checking per-rule traffic statistics in the Envoy metrics. A BackendTrafficPolicy can also target a specific rule, which makes fine-grained traffic policies possible.
GRPCRoute
GRPCRoute routes gRPC traffic natively. It has been in the Standard channel since Gateway API v1.1 and is GA.
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: grpc-route
namespace: app
spec:
parentRefs:
- name: production-gateway
namespace: infra
sectionName: https
hostnames:
- 'grpc.example.com'
rules:
- matches:
- method:
service: myapp.UserService
method: GetUser
backendRefs:
- name: user-service-grpc
port: 50051
- matches:
- method:
service: myapp.OrderService
backendRefs:
- name: order-service-grpc
port: 50051
A Gateway that uses GRPCRoute must support HTTP/2. Envoy Gateway supports HTTP/2 by default, so gRPC routing works without any extra configuration.
BackendTLSPolicy
BackendTLSPolicy is the resource promoted to the Standard channel in Gateway API v1.4, and it configures the TLS connection between the Gateway and the backend Pods. It lets you implement end-to-end encryption from the gateway all the way to the backend.
apiVersion: gateway.networking.k8s.io/v1alpha3
kind: BackendTLSPolicy
metadata:
name: backend-tls
namespace: app
spec:
targetRefs:
- group: ''
kind: Service
name: api-service
validation:
caCertificateRefs:
- group: ''
kind: ConfigMap
name: backend-ca-cert
hostname: api-service.app.svc.cluster.local
This configuration forces the Gateway to use TLS when forwarding a request to api-service, and validates the backend's certificate against the CA certificate stored in the ConfigMap. The hostname field must match the SAN (Subject Alternative Name) of the backend certificate.
Traffic Management Patterns
Canary Deployment (Weight-based Traffic Splitting)
The Gateway API's weight-based traffic splitting is the core mechanism for canary deployments. Set a weight on the backendRefs to control the traffic ratio.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-route
namespace: app
spec:
parentRefs:
- name: production-gateway
namespace: infra
sectionName: https
hostnames:
- 'app.example.com'
rules:
- name: canary-split
matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: app-stable
port: 8080
weight: 90
- name: app-canary
port: 8080
weight: 10
This configuration routes 90% of all traffic to the stable version (app-stable) and 10% to the canary version (app-canary). Once the canary has been validated, adjust the weights progressively to switch over to 0/100.
Combining it with a progressive delivery tool such as Flagger lets you implement metric-driven automatic canary promotion. Flagger supports the Gateway API natively, so it adjusts the HTTPRoute backendRefs weights automatically.
One thing to watch in operations: weight is a relative ratio. weight: 90 and weight: 10 give a 9:1 ratio, and weight: 9 and weight: 1 give the same 9:1 ratio. For readability it is recommended to make the weights sum to 100.
Traffic Mirroring
Traffic mirroring is used to send a copy of production traffic to a test environment so it can be validated against real traffic. The response to mirrored traffic is never returned to the client, so it does not affect production.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: mirror-route
namespace: app
spec:
parentRefs:
- name: production-gateway
namespace: infra
sectionName: https
hostnames:
- 'api.example.com'
rules:
- name: mirror-to-staging
matches:
- path:
type: PathPrefix
value: /api
filters:
- type: RequestMirror
requestMirror:
backendRef:
name: api-staging
port: 8080
backendRefs:
- name: api-production
port: 8080
There is a caveat: you cannot apply multiple RequestMirror filters to a single HTTPRoute rule. If you need to mirror to several destinations, define separate rules, or design the mirroring target service to fan out again.
Header-based Routing
Use header-based routing for A/B tests, or to expose a new version to a specific group of users.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-routing
namespace: app
spec:
parentRefs:
- name: production-gateway
namespace: infra
sectionName: https
hostnames:
- 'app.example.com'
rules:
- name: beta-users
matches:
- headers:
- name: X-Beta-User
value: 'true'
backendRefs:
- name: app-beta
port: 8080
- name: internal-debug
matches:
- headers:
- name: X-Debug-Mode
value: 'enabled'
path:
type: PathPrefix
value: /api
filters:
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: X-Debug-Backend
value: 'debug-v2'
backendRefs:
- name: app-debug
port: 8080
- name: default
matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: app-production
port: 8080
The order of the rules matters. The Gateway API applies the most specific match first, but rules with the same specificity are evaluated in the order they are defined. Place rules with header matching ahead of the default rule.
Rate Limiting (Envoy Gateway BackendTrafficPolicy)
Envoy Gateway supports both global and local rate limiting through the BackendTrafficPolicy CRD. From v1.6 the two can be applied at the same time.
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: api-rate-limit
namespace: app
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: api-route
rateLimit:
type: Global
global:
rules:
- clientSelectors:
- headers:
- name: X-API-Key
type: Distinct
limit:
requests: 1000
unit: Hour
- clientSelectors:
- headers:
- name: X-API-Tier
value: 'premium'
limit:
requests: 10000
unit: Hour
- limit:
requests: 100
unit: Minute
This configuration applies rate limiting in three tiers. First, it allows 1000 requests per hour per distinct value of the X-API-Key header. Second, requests where X-API-Tier is premium are allowed up to 10000 per hour. Third, requests that match none of these conditions are limited to 100 per minute.
Global rate limiting requires a separate Redis-backed rate limit service. You have to set the rateLimit.backend.redis.host value when installing Envoy Gateway with Helm.
# Redis configuration for global rate limiting when installing Envoy Gateway
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.6.3 \
-n envoy-gateway-system --create-namespace \
--set rateLimit.backend.redis.host=redis.infra.svc.cluster.local \
--set rateLimit.backend.redis.port=6379
Local rate limiting runs inside each Envoy instance without Redis, so it is simpler to configure, but you have to account for the fact that the effective allowance varies with the number of Pods. With 3 Envoy Pods and a local limit of 100 per minute, up to 300 per minute may be allowed across the whole cluster.
Installing and Configuring Envoy Gateway
Helm-based Installation
# install the Gateway API CRDs (v1.4.0)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml
# install Envoy Gateway with Helm
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.6.3 \
-n envoy-gateway-system --create-namespace
# check the GatewayClass
kubectl get gatewayclass
# NAME CONTROLLER ACCEPTED
# eg gateway.envoyproxy.io/gatewayclass-controller True
# after creating the Gateway, check that the proxy was provisioned
kubectl get gateway -n infra
kubectl get pods -n envoy-gateway-system
After installation you must check that the GatewayClass ACCEPTED status is True. If it is False, check the controller Pod's logs.
Checking supportedFeatures
From Gateway API v1.4 a supportedFeatures field was added to the GatewayClass status. It lets you check programmatically which features the implementation supports.
# list the features the GatewayClass supports
kubectl get gatewayclass eg -o jsonpath='{.status.supportedFeatures}' | jq .
The result tells you whether features such as HTTPRouteRequestMirror, HTTPRouteBackendTimeout and GRPCRouteListenerHostnameMatching are supported, which is useful for verifying compatibility before you rely on a particular feature in production.
Troubleshooting
When a Route Does Not Attach to the Gateway
The most common problem is an HTTPRoute failing to attach to the Gateway. Diagnose it in the following order.
First, check that the HTTPRoute's parentRefs reference the correct Gateway. If the namespace is omitted it looks for a Gateway in the same namespace, so when referencing a Gateway in another namespace you must specify the namespace.
Second, check the Gateway's allowedRoutes setting. With from: Selector, the namespace containing the HTTPRoute must carry the matching label. Forgetting to add the label to the namespace is a common mistake.
# check the HTTPRoute status
kubectl get httproute api-route -n app -o yaml | yq '.status'
# check the Gateway listener status
kubectl get gateway production-gateway -n infra -o yaml | yq '.status.listeners'
# check and add the namespace label
kubectl get ns app --show-labels
kubectl label ns app gateway-access=true
Third, when sectionName is specified, check that it matches the Gateway listener name exactly.
When Traffic Does Not Reach the Backend
If the Route attached correctly but you still get 502 or 503 errors, inspect the state of the backend service.
# check the backend service and its endpoints
kubectl get svc api-service -n app
kubectl get endpoints api-service -n app
# check the Envoy proxy logs
kubectl logs -n envoy-gateway-system -l app.kubernetes.io/component=proxy --tail=100
# dump the Envoy proxy configuration
kubectl port-forward -n envoy-gateway-system deploy/envoy-production-gateway 19000:19000 &
curl localhost:19000/config_dump | jq '.configs[] | select(.["@type"] | contains("route"))'
If BackendTLSPolicy is applied, check that the backend Pod's TLS certificate is configured correctly and that the hostname matches the certificate's SAN.
When Rate Limiting Does Not Work
The checklist when global rate limiting is not working is as follows. Check the Redis connection state. Check that the rate limit service Pod is running correctly. Check that the BackendTrafficPolicy's targetRefs reference the correct HTTPRoute.
# check the rate limit service status
kubectl get pods -n envoy-gateway-system -l app.kubernetes.io/component=ratelimit
# rate limit service logs
kubectl logs -n envoy-gateway-system -l app.kubernetes.io/component=ratelimit --tail=50
# test the Redis connection
kubectl exec -n envoy-gateway-system deploy/envoy-ratelimit -- redis-cli -h redis.infra.svc.cluster.local ping
Failure Cases and Recovery Procedures
Case 1: Traffic Interruption During a Gateway Update
Changing the listeners on a Gateway resource can restart the Envoy proxy. To guarantee zero downtime when adding or removing a listener in production, follow this procedure.
First, set a PodDisruptionBudget (PDB) to guarantee a minimum number of available Pods. In Envoy Gateway v1.6 you can set the PDB directly through the EnvoyProxy CRD. Make sure there are enough Envoy proxy replicas before the change (at least 2). Perform the listener change during a low-traffic window, and immediately afterwards monitor the Gateway's status.listeners to confirm every listener is Accepted/Programmed.
Case 2: Routing Errors from Deploying a Bad HTTPRoute
Specifying a wrong backendRef in an HTTPRoute, or setting a weight to 0, drops the traffic on that path. The recovery procedure is as follows.
# roll back the problematic HTTPRoute immediately
kubectl rollout undo httproute api-route -n app # HTTPRoute does not support rollout
# instead, reapply the previous version of the manifest
kubectl apply -f httproute-previous-version.yaml
# or, in a GitOps environment, git revert and let it sync automatically
git revert HEAD
git push origin main
HTTPRoute has no rollout feature the way a Deployment does, so you must version your manifests through GitOps (ArgoCD, Flux). The Git history becomes the rollback mechanism.
Case 3: A Traffic Surge When the Rate Limit Redis Fails
If the Redis behind global rate limiting goes down, Envoy by default allows requests when it cannot reach the rate limit service (fail-open). That design puts availability first, but the backend may not be able to absorb the sudden increase in traffic.
As a countermeasure, apply global and local rate limiting together. Local rate limiting does not depend on Redis, so basic traffic protection survives a Redis outage. Redis must be deployed in a high-availability configuration (Redis Sentinel or Redis Cluster).
Operations Checklist
Pre-deployment Checklist
- Confirm the GatewayClass ACCEPTED status is True
- Confirm every Gateway listener is Accepted/Programmed
- Confirm the HTTPRoute/GRPCRoute parentRef references the correct Gateway and sectionName
- Confirm the TLS certificates have enough time before expiry (with cert-manager, verify the auto-renewal configuration)
- Confirm the BackendTLSPolicy hostname matches the backend certificate SAN
- Confirm the Redis connection state when rate limiting is configured
- Confirm the Envoy proxy HPA configuration can absorb the expected traffic
Monitoring Checklist
- Monitor the Gateway resource's status.conditions periodically
- Collect the Envoy proxy metrics (request count, latency, error rate) into Prometheus/Grafana
- Monitor the rate limit hit count to verify the thresholds are appropriate
- Configure TLS certificate expiry alerts (at least 14 days ahead)
- Monitor the Envoy proxy Pods' resource usage (CPU, memory)
Upgrade Checklist
- Check the compatibility matrix between the Gateway API CRD version and the Envoy Gateway version
- Upgrade the CRDs first, then the controller
- Back up the existing resources before upgrading (
kubectl get gateway,httproute,grpcroute -A -o yaml) - Validate the upgrade in a staging environment first
- After the upgrade, confirm every Route's attach status and that traffic flows normally
Migration-from-Ingress Checklist
- Convert the existing Ingress resources into Gateway API resources with the
ingress2gatewaytool - Review the converted resources by hand to confirm the annotation-based settings mapped correctly
- Run Ingress and the Gateway API side by side and shift traffic over gradually
- Do the DNS switch as the last step, and keep the old Ingress around for at least 1 week for rollback
# conversion using the ingress2gateway tool
go install github.com/kubernetes-sigs/ingress2gateway@latest
ingress2gateway print --providers ingress-nginx --all-namespaces
# save the conversion output to a file and review it
ingress2gateway print --providers ingress-nginx --all-namespaces > gateway-resources.yaml
Conclusion
As of 2026 the Gateway API is firmly established as the new standard for Kubernetes networking. With BackendTLSPolicy, Named Rules and supportedFeatures promoted to the Standard channel in v1.4, its reliability in production has risen considerably. Envoy Gateway v1.6 has the features to meet enterprise requirements: applying global and local rate limiting together, extending SecurityPolicy to TCPRoute, and mTLS configuration.
If you are planning a switch from an existing Ingress, an incremental migration using the ingress2gateway tool is recommended. The Gateway API and the existing Ingress can coexist in the same cluster, so you can switch over safely one service at a time. The most important things are versioning your manifests through GitOps and building an adequate monitoring and alerting system.
References
- Gateway API official documentation
- Gateway API v1.4 release blog
- Envoy Gateway official documentation
- Envoy Gateway v1.6.0 release notes
- Envoy Gateway rate limiting guide
- Guide to migrating from Ingress to the Gateway API
- Envoy Gateway 1.3 release highlights (Tetrate)
- A case study migrating from NGINX Ingress to Envoy Gateway (Qovery)
- List of Gateway API implementations