LabHub

Blog

Kubernetes Gateway API and Envoy Traffic Management 2026

한국어English日本語

Kubernetes Gateway API and Envoy Gateway Traffic Management in Practice 2026

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.

ComparisonIngressGateway API
Protocol supportHTTP/HTTPS onlyHTTP, gRPC, TCP, UDP and TLS all supported
Routing expressivenessHost/path based onlyMatching on headers, query parameters and methods
Role separationEverything in a single resourceRoles split across GatewayClass, Gateway, Route
Vendor lock-inAnnotation-based extension (vendor-specific)Standardized CRD-based extension (portable)
Traffic splittingNot natively supportedWeight-based traffic splitting natively supported
TLS managementBasic TLS termination onlyBackend mTLS supported through BackendTLSPolicy
Multi-tenancyLimitedCross-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.

ImplementationData planeGateway API versionKey characteristics
Envoy GatewayEnvoy Proxyv1.2+Native rate limiting, SecurityPolicy, AI Gateway extensions
Istio (Ambient)Envoy Proxy / ztunnelv1.1+Service mesh integration, no sidecar in Ambient mode
NGINX Gateway FabricNGINXv1.2+NGINX based, familiar to existing NGINX users
Cilium GatewayeBPF / Envoyv1.1+eBPF-based L4 acceleration, network policy integration
Kong GatewayKong Proxyv1.2+Plugin ecosystem, API management integration
TraefikTraefik Proxyv1.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

Monitoring Checklist

Upgrade Checklist

Migration-from-Ingress Checklist

# 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

Comments

No comments yet.

Sign in to leave a comment