Lesson 08 of 15 · Traffic Security
Gateway API
The successor to Ingress: GatewayClass, Gateway and HTTPRoute with a clean split between platform and app teams, TLS on listeners, safe cross-namespace attachment, and weighted traffic splitting.
Why a new API
Ingress mixed everyone's concerns into one object, and pushed anything beyond basic HTTP routing into controller-specific annotations. Gateway API splits responsibilities across objects and standardises the features that used to be annotations: traffic splitting, header matching, TLS options, TCP/UDP/gRPC routes.
Think of an airport. The airport authority decides which terminal building exists (GatewayClass). The terminal manager opens gates, sets up security checks, and decides which airlines may use which gates (Gateway). Each airline decides its own flights and destinations (HTTPRoute). Nobody needs to edit anyone else's paperwork.
The roles and objects
| Object | Owner | Contains |
|---|---|---|
GatewayClass |
Infrastructure provider | Which controller implements it (Envoy Gateway, Cilium, Istio, Traefik, a cloud LB…) |
Gateway |
Platform / cluster operators | Listeners: port, protocol, hostname, TLS certificates, which namespaces may attach |
HTTPRoute (and GRPCRoute, TLSRoute…) |
App teams | Matches (host, path, headers) → backends, with weights and filters |
ReferenceGrant |
Owner of the referenced namespace | Opt-in for cross-namespace references |
Install the CRDs from the Gateway API releases (standard channel), then an implementation. Most implementations document the exact version they support:
$ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/<version>/standard-install.yaml
A shared Gateway, owned by the platform
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: public
namespace: infra
spec:
gatewayClassName: envoy-gateway # from your implementation
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "*.example.com"
tls:
mode: Terminate
certificateRefs:
- name: wildcard-example-com-tls # a cert-manager Certificate in 'infra'
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: public # only namespaces with this label may attach
A Route, owned by the app team
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: shop
namespace: shop # labelled gateway-access=public
spec:
parentRefs:
- name: public
namespace: infra
sectionName: https
hostnames: [ "shop.example.com" ]
rules:
- matches:
- path: { type: PathPrefix, value: /api }
backendRefs:
- name: api
port: 8080
- backendRefs:
- name: web
port: 80
The app team never touches the certificate or the listener; the platform team never touches the app's paths.
Traffic splitting (canary releases)
rules:
- backendRefs:
- name: web-v1
port: 80
weight: 90
- name: web-v2
port: 80
weight: 10
Shift weights gradually (manually, or with a progressive delivery tool) and roll back instantly by moving the weight back.
Debug with status
$ kubectl -n shop describe httproute shop
Status:
Parents:
Conditions:
Type: Accepted
Status: False
Reason: NotAllowedByListeners
NotAllowedByListeners → the shop namespace lacks the gateway-access=public label the Gateway requires. Gateway API resources report why in their status, which is much friendlier than silent annotations.
Try it: Gateway API on kind
- Install the Gateway API CRDs and an implementation (for example Envoy Gateway, following its quickstart; it also creates a GatewayClass).
- Create the
infranamespace, a cert-manager Certificate for*.example.comfrom your private CA, and thepublicGateway. - Deploy
webinshop, apply the HTTPRoute, and read its status. Fix theNotAllowedByListenerscondition by labelling the namespace. - Port-forward to the gateway's Service and test with
curl -k --resolve shop.example.com:8443:127.0.0.1 https://shop.example.com:8443/. - Deploy
web-v2and split traffic 90/10; count responses over 100 requests.
Going deeper: Gateway API in platforms
- Implementations differ in supported features; each publishes conformance results. Check them before relying on a feature.
- Policy attachment (timeouts, retries, backend TLS, rate limits) is evolving; some policies are standard, others implementation-specific CRDs.
- Service meshes (Istio, Linkerd, Cilium) use Gateway API for east-west traffic as well (the GAMMA initiative), so one API covers both edge and in-cluster routing.
- Migrating from Ingress: tools such as ingress2gateway translate Ingress objects to Gateway API resources as a starting point.
Recap
- Gateway API = GatewayClass (infra) → Gateway (platform: listeners, TLS, allowed namespaces) → Routes (apps).
allowedRoutesand ReferenceGrant make cross-namespace access explicit and safe.- Weighted
backendRefsgive standard traffic splitting. - Read status conditions (Accepted, ResolvedRefs) to debug.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.