The three-layer model
- GatewayClass — cluster admin defines which implementation provides Gateways (e.g. "use Envoy Gateway", "use nginx Gateway Fabric"). Set once per cluster, per implementation.
- Gateway — platform team creates one or more. Each Gateway provisions an actual load balancer + listeners (port + protocol). Multiple namespaces / app teams attach Routes to it.
- HTTPRoute / TCPRoute / TLSRoute / GRPCRoute — app team writes these in their own namespace. Each says "for traffic arriving at gateway X on hostname Y, route to my Service."
The role separation is the killer feature. With Ingress, the platform team and app teams fought over the same Ingress resource. Gateway API gives each role its own resource type with appropriate RBAC.
Install (Envoy Gateway as an example)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
# Install an implementation (Envoy Gateway)
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.2.0 \
-n envoy-gateway-system --create-namespace
# The implementation auto-creates a GatewayClass
kubectl get gatewayclass
Other implementations: nginx Gateway Fabric, Traefik 3.x, Cilium (when its gateway support is enabled), Istio's own Gateway API conformance, HAProxy. Most major ingress controllers now support Gateway API alongside Ingress.
The minimum-viable example
# 1. The Gateway (platform team)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: external
namespace: ingress
spec:
gatewayClassName: eg
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All # allow HTTPRoutes from any namespace to attach
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: example-com-tls
allowedRoutes:
namespaces:
from: All
---
# 2. An HTTPRoute (app team, in their own namespace)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app-routes
namespace: my-app
spec:
parentRefs:
- name: external
namespace: ingress
sectionName: https
hostnames: [ "app.example.com" ]
rules:
- matches:
- path: { type: PathPrefix, value: "/api" }
backendRefs:
- name: api-service
port: 8080
- matches:
- path: { type: PathPrefix, value: "/" }
backendRefs:
- name: web-service
port: 3000
Apply both. The Gateway provisions a LoadBalancer (or whatever the implementation uses); the HTTPRoute attaches its routing rules; app.example.com resolves through the gateway with path-based routing.
What HTTPRoute can express
spec:
hostnames: [ "api.example.com" ]
rules:
# Match by path + header + method
- matches:
- path: { type: PathPrefix, value: "/v1/" }
method: GET
headers:
- name: X-API-Version
value: "1.0"
backendRefs:
- name: api-v1
port: 8080
# Header-based routing for canary / experiments
- matches:
- path: { type: PathPrefix, value: "/api/" }
headers:
- name: X-Beta-User
value: "true"
backendRefs:
- name: api-beta
port: 8080
# Weighted backend routing for A/B / canary
- matches:
- path: { type: PathPrefix, value: "/api/" }
backendRefs:
- name: api-stable
port: 8080
weight: 95
- name: api-canary
port: 8080
weight: 5
# Request mutation
- matches:
- path: { type: PathPrefix, value: "/legacy/" }
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
set:
- { name: X-Legacy-Route, value: "true" }
- type: URLRewrite
urlRewrite:
path: { type: ReplacePrefixMatch, replacePrefixMatch: "/api/v1/" }
backendRefs:
- name: api-v1
port: 8080
TLS termination + cert-manager
Gateway API works directly with cert-manager (see step-ca tutorial's cert-manager pattern):
# cert-manager creates a Certificate resource that maps to the Gateway's Secret
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com-tls
namespace: ingress
spec:
secretName: example-com-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames: [ "app.example.com", "api.example.com" ]
The cert renews automatically; the Gateway picks up changes; no annotations needed.
Cross-namespace ReferenceGrants
For security: by default, an HTTPRoute in namespace A can only reference Services / Secrets in namespace A. To allow cross-namespace references (e.g. central TLS secret), the target namespace must explicitly grant permission:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: ingress-to-tls-secret
namespace: ingress
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: ingress
to:
- group: ""
kind: Secret
Explicit grant model prevents Ingress's "anyone can claim any hostname" cross-namespace footgun.
TCP / TLS / gRPC routes
# Raw TCP (forward port-to-service, no protocol parsing)
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: postgres
spec:
parentRefs:
- name: tcp-gateway
sectionName: postgres-listener
rules:
- backendRefs:
- name: postgres
port: 5432
# TLS pass-through (SNI-routed; cert lives at the backend)
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: prometheus-tls
spec:
parentRefs:
- name: tls-gateway
hostnames: [ "prom.example.com" ]
rules:
- backendRefs:
- name: prometheus
port: 443
# gRPC (HTTP/2 with gRPC-specific matchers)
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: my-service-grpc
spec:
parentRefs: [ { name: grpc-gateway } ]
hostnames: [ "grpc.example.com" ]
rules:
- matches:
- method: { service: my.package.UserService, method: GetUser }
backendRefs:
- { name: user-service-grpc, port: 50051 }
Migration from Ingress
The ingress2gateway CLI translates Ingress + annotations to Gateway API resources:
go install github.com/kubernetes-sigs/ingress2gateway@latest
ingress2gateway print --providers ingress-nginx,kong --input-file ingress.yaml > gateway.yaml
Translation is mechanical for simple cases; provider-specific annotations may need manual review.
Why this matters
- Portability. One config works across nginx Gateway Fabric, Envoy Gateway, Traefik, Cilium, Istio. With Ingress, switching controllers meant rewriting annotations.
- Role separation. Cluster admin owns GatewayClass; platform owns Gateway; app teams own Routes. Each has its own RBAC.
- Real-protocol support. HTTP / gRPC / TCP / TLS-passthrough are first-class, not annotation hacks.
- Standards body. SIG-Network-managed; conformance tests; predictable evolution.
When Ingress is still fine
- Existing setups that work, with stable team-knowledge of the specific Ingress controller. Don't rewrite for the sake of it.
- Very simple use cases (one hostname, one service) where Gateway API adds CRD overhead for little gain.
- Controllers that don't yet support Gateway API at the conformance level you need.
For new clusters or significant rewrites in 2026, start with Gateway API. The Ingress resource is deprecated in spirit (still works, no new features); Gateway API is what Kubernetes networking is evolving into.