Kubernetes Gateway API

Introduction

The classic Kubernetes Ingress API has served the community well, but it was never designed for everything we now need from HTTP routing: traffic splitting, header-based routing, gRPC, TLS passthrough, cross-namespace references, or multi-team access controls. Gateway API (gateway.networking.k8s.io) was designed from scratch to solve all of that, and as of v1.5.0 it is the standard way to expose services in Kubernetes.

I made the switch from Ingress to Gateway API about a year ago and I have not looked back. This article covers everything I use day-to-day: GatewayClass, Gateway, HTTPRoute, GRPCRoute, TLSRoute, ReferenceGrant, and policy attachments β€” all with the Go microservice from the previous article as the backend.


Why Replace Ingress?

Capability
Ingress
Gateway API

Role separation (infra vs dev)

No

Yes

Traffic splitting (weight-based)

Annotation hack

Native

Header/method-based routing

Annotation hack

Native

gRPC support

No

GRPCRoute (standard)

TLS termination vs passthrough

Annotation hack

TLSRoute

Cross-namespace routing

No

ReferenceGrant

Multi-protocol (TCP/UDP)

No

TCPRoute/UDPRoute (experimental)

Portable YAML across controllers

No (annotations differ)

Yes

Ingress annotations are implementation-specific. Gateway API is portable across Nginx, Istio, Envoy Gateway, HAProxy, and Cilium.


Installing Gateway API CRDs (v1.5.0)

Gateway API CRDs are not included in Kubernetes core β€” install them separately.

Standard Channel (production-ready)

Standard channel includes: GatewayClass, Gateway, HTTPRoute, GRPCRoute, TLSRoute, ReferenceGrant, BackendTLSPolicy.

Expected output:

Experimental Channel (additional resources)

Experimental channel adds TCPRoute, UDPRoute, BackendTrafficPolicy.

Install a Gateway Controller

The CRDs define the API. You still need a controller. I use Envoy Gateway for standalone clusters:

For Istio environments, the Istio istiod controller handles Gateway API natively since Istio 1.18.


The Role Model

Gateway API is designed around three personas β€” this maps directly to real team structures:

Persona
Typical Role
Manages

Ian (Infrastructure Provider)

Platform SRE

GatewayClass β€” defines which controller handles traffic

Chihiro (Cluster Operator)

Cluster Admin

Gateway β€” defines listeners (port, protocol, TLS certs)

Ana (Developer)

Service Owner

HTTPRoute / GRPCRoute β€” attaches routes to a Gateway

This separation means developers can configure their own routing without cluster admin access β€” they just create HTTPRoutes in their namespace.


GatewayClass

GatewayClass is a cluster-scoped resource defining the controller type. One per controller implementation.


Gateway

Gateway describes a traffic entry point. Cluster operators create Gateways. Each Gateway has one or more Listeners.

Label the production namespace so routes can attach:

Create the TLS secret:


HTTPRoute

HTTPRoute is the core routing resource for L7 HTTP traffic. Developers create HTTPRoutes in their own namespace.

Basic Path Routing

Header and Method Matching

Traffic Splitting (Canary Deployment)

Gradually shift weight from 90/10 β†’ 50/50 β†’ 0/100 as you gain confidence.

HTTP Redirect

Request/Response Header Manipulation


GRPCRoute

GRPCRoute handles gRPC traffic (stable since Gateway API v1.1). Here I extend the Go microservice with a gRPC server.

main.go additions for gRPC:

GRPCRoute YAML:


TLSRoute

TLSRoute handles TLS passthrough β€” the Gateway does not terminate TLS, it forwards the TCP stream based on SNI (Server Name Indication).


Cross-Namespace References with ReferenceGrant

By default, a Route in namespace production cannot reference a Service in namespace shared-services. ReferenceGrant explicitly allows it.


BackendTLSPolicy

BackendTLSPolicy configures TLS from the Gateway to the backend service (not to be confused with TLSRoute which is client-facing).


Inspecting Gateway API Resources

The .status.parents[].conditions field tells you whether the route attached successfully β€” this is the first place to look when traffic is not flowing.


Complete Example: Deploying the Go Microservice

Putting it all together with namespace, Deployment, Service, HTTPRoute:


Migration from Ingress

If you have existing Ingress resources, migrate iteratively:

  1. Install Gateway API CRDs and a controller

  2. Create a GatewayClass and Gateway

  3. Create equivalent HTTPRoutes alongside existing Ingress

  4. Switch DNS to the new Gateway's IP

  5. Remove the Ingress resources

There is no automated migration tool β€” the mapping is straightforward but manual.

Example Ingress β†’ HTTPRoute mapping:


What I Learned

  • GatewayClass/Gateway belongs to the platform team; routes belong to app teams β€” this role separation is the most practical benefit of Gateway API

  • Traffic splitting replaces most blue/green tooling β€” weight: 90/10 on canary is cleaner than maintaining two Ingress resources

  • GRPCRoute is zero-config compared to Ingress annotations for gRPC

  • Always check .status.parents[].conditions. When routes do not attach, the reason is always there

  • ReferenceGrant is explicit security β€” cross-namespace access requires deliberate consent from the target namespace owner


Next

Last updated