Skip to main content

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:

RoleResourceResponsibility
Infrastructure ProviderGatewayClassDefines available gateway implementations
Cluster OperatorGatewayConfigures load balancers, TLS, listeners
Application DeveloperHTTPRoute / TLSRouteDefines 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 kubectl access.
  • helm installed.
  • At least one IP reserved in your cluster's LoadBalancer pool - the Gateway is exposed through a LoadBalancer Service 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.


Last updated on June 26, 2026.