Kubernetes Gateway API
1. Why Gateway API Exists
When applications run in Kubernetes, users must be able to access them from outside, and services inside the cluster must communicate with each other.
For a long time, Kubernetes used Ingress to expose applications. Ingress worked, but as systems grew larger and teams increased, many real problems appeared.
To solve these problems, Kubernetes introduced the Gateway API.
2. Understanding Traffic in Kubernetes
Before learning Gateway API, we must understand how traffic moves.
2.1 North–South Traffic
North–South traffic means:
- Traffic coming into the cluster from outside
- Traffic going out of the cluster
Examples:
- User opens a website in a browser
- Mobile app calls an API in Kubernetes
- External system connects to a service
This is external traffic.
2.2 East–West Traffic
East–West traffic means:
- Traffic inside the cluster
- Service talking to another service
Examples:
- Frontend calls backend
- Order service calls payment service
This is internal traffic.
Important Note:
- Ingress and Gateway API → handle North–South traffic
- Service Mesh → handles East–West traffic
3. What Is Ingress?
Ingress is a Kubernetes object that:
- Exposes services outside the cluster
- Routes traffic using hostnames and paths
Example:
/ → frontend service
/api → backend service
Ingress worked well for:
- Small clusters
- Single-team setups
- Basic routing
4. Problems with Ingress (Why Change Was Needed)
As Kubernetes moved to production at scale, the following issues became apparent:
- Everything in one file - Load balancer config, TLS config, routing rules - all mixed together.
- Annotations everywhere - Important logic hidden in annotations.
- Hard to read and debug - Large, complex Ingress manifests are difficult to maintain.
- No clear ownership - Application teams could modify infrastructure settings, creating risk in multi-team clusters.
- Different behavior per controller - The same YAML can behave differently with NGINX, ALB, and other controllers.
- Security concerns and retirement - The widely-used Ingress NGINX controller is being retired and will no longer receive updates or security patches after March 2026, making it unsuitable for long-term production use.
See the official retirement announcement: https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/
Ingress was simple, but not safe or scalable for large environments.
5. What Is Gateway API?
Gateway API is a new Kubernetes networking API designed to fix the limitations of Ingress.
It provides:
- Clear separation of responsibilities
- Standard behavior across implementations
- Safer configuration for production
- Support for advanced routing
Gateway API separates concerns across three roles:
| Role | Resource | Responsibility |
|---|---|---|
| Infrastructure Provider | GatewayClass | Defines available gateway implementations |
| Cluster Operator | Gateway | Configures load balancers, TLS, listeners |
| Application Developer | HTTPRoute / TLSRoute | Defines application routing rules |
6. Implementation Guide
The Gateway API is a set of standard APIs, but the cluster still needs an implementation (a controller) to act on them. This guide uses Envoy Gateway.
Prerequisites
- An active E2E Kubernetes cluster with
kubectlaccess. helminstalled.- At least one IP reserved in your cluster's LoadBalancer pool - the Gateway is exposed through a
LoadBalancerService and consumes one pool IP. See Assigning an External IP.
Step 1: Install the Gateway API CRDs and Envoy Gateway
The Gateway API CRDs are not installed on an E2E cluster by default. The Envoy Gateway Helm chart installs both the CRDs and the controller:
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.6.1 \
-n envoy-gateway-system \
--create-namespace
Wait for it to become available:
kubectl wait --timeout=5m -n envoy-gateway-system \
deployment/envoy-gateway --for=condition=Available
Step 2: Create a GatewayClass
The GatewayClass tells Kubernetes which controller manages your Gateways. Save as gatewayclass.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy-gateway-class
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
kubectl apply -f gatewayclass.yaml
kubectl get gatewayclass
The ACCEPTED column should read True.
Step 3: Create a Gateway
The Gateway provisions the actual load balancer and listeners. Save as gateway.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: api-gateway
namespace: default
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
kubectl apply -f gateway.yaml
kubectl get gateway
Envoy Gateway creates a LoadBalancer Service for this Gateway, and E2E's MetalLB assigns it an IP from your reserved pool. Once an IP is attached, the Gateway's ADDRESS is populated and PROGRAMMED reads True.
Step 4: Deploy a Backend and Route Traffic
Deploy a sample app and expose it with an HTTPRoute. Save as app.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-1
spec:
replicas: 1
selector:
matchLabels:
app: backend-1
template:
metadata:
labels:
app: backend-1
spec:
containers:
- name: backend-1
image: nginxdemos/hello:plain-text
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: backend-1
spec:
selector:
app: backend-1
ports:
- port: 80
targetPort: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: host-route
spec:
parentRefs:
- name: api-gateway
namespace: default
hostnames:
- app.yourdomain.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: backend-1
port: 80
kubectl apply -f app.yaml
kubectl get httproute
Step 5: Test
Point a DNS A record for app.yourdomain.com at the Gateway's ADDRESS, or test directly with a Host header:
GATEWAY_IP=$(kubectl get gateway api-gateway -o jsonpath='{.status.addresses[0].value}')
curl -H "Host: app.yourdomain.com" http://$GATEWAY_IP/
A request whose host matches the HTTPRoute is served by backend-1; a request with an unknown host returns 404 from the Gateway.
7. Adding HTTPS
To terminate TLS, add an https listener with a certificateRefs entry to the Gateway, and provision the referenced certificate with cert-manager. For an end-to-end example that combines a TLS-enabled Gateway, cert-manager, and HTTPRoutes, see the Argo CD guide, which uses this same Gateway API setup.