In this episode, we'll explore the latest update to Kubernetes Ingress using API Gateway. Learn how to leverage the new Gateway API for more advanced routing, traffic management, and modern ingress patterns.

Note
If you want to read the previous episode, you can click the Episode 19 thumbnail below
In the previous episode, we learned about traditional Kubernetes Ingress for HTTP/HTTPS routing. Now in episode 19.1, we'll explore the Gateway API - the next generation of Ingress that provides more expressive, extensible, and role-oriented APIs for managing ingress traffic.
Note: Here I'll be using a Kubernetes Cluster installed through K3s.
The Gateway API is not a replacement for Ingress, but rather an evolution that addresses limitations and provides more advanced capabilities. It's designed to be more expressive, extensible, and portable across different implementations.
Gateway API is a collection of Kubernetes resources that model service networking in Kubernetes. It's the successor to the Ingress API, offering improved expressiveness, extensibility, and role-oriented design.
Gateway API was developed by the Kubernetes SIG Network community and graduated to Beta in 2022, with GA (General Availability) for core features in 2023. It provides a more powerful and flexible way to manage ingress traffic compared to traditional Ingress.
Key characteristics of Gateway API:
Let's understand the key differences:
| Aspect | Gateway API | Traditional Ingress |
|---|---|---|
| Design | Role-oriented (3 personas) | Single resource |
| Expressiveness | Highly expressive | Limited |
| Protocols | HTTP, HTTPS, TCP, UDP, gRPC | HTTP, HTTPS only |
| Routing | Header, query, method-based | Host and path only |
| Traffic Management | Built-in traffic splitting | Requires annotations |
| Extensibility | Custom filters and resources | Annotation-based |
| Portability | Standardized across providers | Provider-specific |
| Maturity | GA (2023) | GA (2019) |
Gateway API introduces three main resources representing different personas:
Gateway API ConceptsDefines a class of Gateways (like IngressClass). Managed by infrastructure providers.
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: example-gateway-class
spec:
controllerName: example.com/gateway-controllerDefines how traffic is translated to Services. Managed by cluster operators.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: example-gateway
spec:
gatewayClassName: example-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80Defines HTTP routing rules. Managed by application developers.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: example-route
spec:
parentRefs:
- name: example-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080sudo kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yamlVerify installation:
sudo kubectl get crd | grep gatewayOutput:
gatewayclasses.gateway.networking.k8s.io
gateways.gateway.networking.k8s.io
httproutes.gateway.networking.k8s.io
referencegrants.gateway.networking.k8s.ioFor this example, we'll use NGINX Gateway Fabric:
sudo kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.1.0/crds.yaml
sudo kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.1.0/nginx-gateway.yamlVerify controller:
sudo kubectl get pods -n nginx-gatewayapiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: nginx
spec:
controllerName: gateway.nginx.org/nginx-gateway-controllerApply:
sudo kubectl apply -f gateway-class.ymlapiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: example-gateway
namespace: default
spec:
gatewayClassName: nginx
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: SameApply:
sudo kubectl apply -f gateway.ymlVerify:
sudo kubectl get gatewayOutput:
NAME CLASS ADDRESS READY AGE
example-gateway nginx 203.0.113.10 True 30sapiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80Apply:
sudo kubectl apply -f app.ymlapiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-route
spec:
parentRefs:
- name: example-gateway
hostnames:
- "web.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-service
port: 80Apply:
sudo kubectl apply -f http-route.ymlVerify:
sudo kubectl get httprouteNow traffic to web.example.com routes through the Gateway to web-service.
Tip
If you are using K3s, which comes with Traefik as the default Ingress Controller, and you want to try running the Gateway API, you can follow these steps:
sudo nvim /etc/rancher/k3s/config.yamlsudo systemctl restart k3skubeconfig so you don’t need to use sudomkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/confighelm repo add traefik https://traefik.github.io/charts
helm repo updateapiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: web-gateway
spec:
gatewayClassName: traefik
listeners:
- name: http
protocol: HTTP
port: 8000
hostname: "*.example.com"
- name: https
protocol: HTTPS
port: 8443
hostname: web.example.com
tls:
mode: Terminate
certificateRefs:
- name: example-tlsDeployment, Service, and HTTPRoute# ================================
# Web Service
# ================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-service
spec:
replicas: 3
selector:
matchLabels:
app: web-service
template:
metadata:
labels:
app: web-service
spec:
containers:
- name: web-service
image: ghcr.io/armandwipangestu/web-service:1.0.1
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web-service
ports:
- port: 80
targetPort: 3000
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-service
spec:
parentRefs:
- name: web-gateway
sectionName: https
- name: web-gateway
sectionName: http
hostnames:
- "web.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-service
port: 80Test the connection
curl -k --resolve web.example.com:443:10.10.10.4 https://web.example.com -vRoute based on HTTP headers:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-route
spec:
parentRefs:
- name: example-gateway
rules:
- matches:
- headers:
- name: X-API-Version
value: v2
backendRefs:
- name: api-v2-service
port: 8080
- matches:
- headers:
- name: X-API-Version
value: v1
backendRefs:
- name: api-v1-service
port: 8080Route based on query parameters:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: query-route
spec:
parentRefs:
- name: example-gateway
rules:
- matches:
- queryParams:
- name: version
value: beta
backendRefs:
- name: beta-service
port: 8080
- matches:
- queryParams:
- name: version
value: stable
backendRefs:
- name: stable-service
port: 8080Route based on HTTP methods:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: method-route
spec:
parentRefs:
- name: example-gateway
rules:
- matches:
- method: POST
path:
type: PathPrefix
value: /api
backendRefs:
- name: write-service
port: 8080
- matches:
- method: GET
path:
type: PathPrefix
value: /api
backendRefs:
- name: read-service
port: 8080Gateway API makes traffic splitting simple:
This sends 90% of traffic to alpha version and 10% to beta version.
# Phase 1: 5% canary
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
spec:
parentRefs:
- name: example-gateway
rules:
- backendRefs:
- name: stable-service
port: 80
weight: 95
- name: canary-service
port: 80
weight: 5Update weights progressively: 5% → 25% → 50% → 100%
Configure HTTPS with Gateway API:
sudo kubectl create secret tls example-tls \
--cert=path/to/tls.crt \
--key=path/to/tls.keyapiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tls-gateway
spec:
gatewayClassName: nginx
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: example-tls
allowedRoutes:
namespaces:
from: SameapiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-route
spec:
parentRefs:
- name: tls-gateway
hostnames:
- "secure.example.com"
rules:
- backendRefs:
- name: web-service
port: 80Gateway API supports routing across namespaces with ReferenceGrant:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-gateway-routes
namespace: app-namespace
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: default
to:
- group: ""
kind: ServiceapiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: cross-ns-route
namespace: default
spec:
parentRefs:
- name: example-gateway
rules:
- backendRefs:
- name: app-service
namespace: app-namespace
port: 8080Modify requests and responses:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: header-mod-route
spec:
parentRefs:
- name: example-gateway
rules:
- filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Custom-Header
value: custom-value
set:
- name: X-Forwarded-Proto
value: https
remove:
- X-Internal-Header
backendRefs:
- name: api-service
port: 8080apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: rewrite-route
spec:
parentRefs:
- name: example-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /old-api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /new-api
backendRefs:
- name: api-service
port: 8080Mirror traffic to another service for testing:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: mirror-route
spec:
parentRefs:
- name: example-gateway
rules:
- filters:
- type: RequestMirror
requestMirror:
backendRef:
name: test-service
port: 8080
backendRefs:
- name: production-service
port: 8080Traffic goes to production-service, but is also mirrored to test-service.
Configure request timeouts:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: timeout-route
spec:
parentRefs:
- name: example-gateway
rules:
- timeouts:
request: 30s
backendRequest: 25s
backendRefs:
- name: slow-service
port: 8080apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: microservices-gateway
spec:
gatewayClassName: nginx
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "*.example.com"
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-routes
spec:
parentRefs:
- name: microservices-gateway
hostnames:
- "api.example.com"
rules:
# User service with canary
- matches:
- path:
type: PathPrefix
value: /users
backendRefs:
- name: user-service-stable
port: 8080
weight: 90
- name: user-service-canary
port: 8080
weight: 10
# Order service with header routing
- matches:
- path:
type: PathPrefix
value: /orders
headers:
- name: X-API-Version
value: v2
backendRefs:
- name: order-service-v2
port: 8080
- matches:
- path:
type: PathPrefix
value: /orders
backendRefs:
- name: order-service-v1
port: 8080
# Product service with timeout
- matches:
- path:
type: PathPrefix
value: /products
timeouts:
request: 10s
backendRefs:
- name: product-service
port: 8080
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-routes
spec:
parentRefs:
- name: microservices-gateway
hostnames:
- "web.example.com"
rules:
- backendRefs:
- name: frontend-service
port: 3000sudo kubectl get gateway example-gateway -o yamlLook for status conditions:
status:
conditions:
- type: Accepted
status: "True"
- type: Programmed
status: "True"
addresses:
- value: 203.0.113.10sudo kubectl get httproute web-route -o yamlsudo kubectl describe gateway example-gatewayapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
spec:
parentRefs:
- name: example-gateway
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: app-service
port: 80Separate concerns by persona:
# Infrastructure team manages GatewayClass
# Cluster operators manage Gateway
# Developers manage HTTPRouteUse for canary deployments:
backendRefs:
- name: stable
weight: 95
- name: canary
weight: 5Always set appropriate timeouts:
timeouts:
request: 30s
backendRequest: 25sControl cross-namespace access:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrantCheck status regularly:
sudo kubectl get gateway -o wide
sudo kubectl get httproute -o wideChoose clear resource names:
# Good
name: api-gateway
name: user-service-route
# Avoid
name: gw1
name: routeCheck controller logs:
sudo kubectl logs -n nginx-gateway -l app=nginx-gatewayVerify parent reference:
sudo kubectl get httproute <route-name> -o yamlCheck status conditions.
Verify ReferenceGrant:
sudo kubectl get referencegrant -ACheck secret exists:
sudo kubectl get secret <tls-secret-name>In episode 19.1, we've explored the Gateway API - the next generation of Kubernetes ingress. We've learned how it improves upon traditional Ingress with more expressive routing, better extensibility, and role-oriented design.
Key takeaways:
Gateway API represents the future of ingress in Kubernetes. While traditional Ingress remains supported, Gateway API provides more powerful capabilities for modern cloud-native applications. Consider adopting Gateway API for new projects to leverage its advanced features.
Ready to modernize your Kubernetes ingress? Keep learning and look forward to the next episode!
Note
If you want to continue to the next episode, you can click the Episode 20 thumbnail below