Learning Kubernetes - Episode 19.1 - Ingress with Gateway API

Learning Kubernetes - Episode 19.1 - Ingress with Gateway API

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.

Arman Dwi Pangestu
Arman Dwi PangestuMarch 23, 2026
0 views
10 min read

Introduction

Note

If you want to read the previous episode, you can click the Episode 19 thumbnail below

Episode 19Episode 19

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.

What Is Gateway API?

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:

  • Role-oriented - Separates concerns between infrastructure and application teams
  • Expressive - More routing capabilities than Ingress
  • Extensible - Custom resources and filters
  • Portable - Works across different implementations
  • Type-safe - Strongly typed API with validation
  • Multi-protocol - HTTP, HTTPS, TCP, UDP, gRPC
  • Advanced routing - Header-based, query param-based routing
  • Traffic splitting - Weighted routing for canary deployments

Gateway API vs Traditional Ingress

Let's understand the key differences:

AspectGateway APITraditional Ingress
DesignRole-oriented (3 personas)Single resource
ExpressivenessHighly expressiveLimited
ProtocolsHTTP, HTTPS, TCP, UDP, gRPCHTTP, HTTPS only
RoutingHeader, query, method-basedHost and path only
Traffic ManagementBuilt-in traffic splittingRequires annotations
ExtensibilityCustom filters and resourcesAnnotation-based
PortabilityStandardized across providersProvider-specific
MaturityGA (2023)GA (2019)

Gateway API Architecture

Gateway API introduces three main resources representing different personas:

1. GatewayClass

Defines a class of Gateways (like IngressClass). Managed by infrastructure providers.

Kubernetesyml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
    name: example-gateway-class
spec:
    controllerName: example.com/gateway-controller

2. Gateway

Defines how traffic is translated to Services. Managed by cluster operators.

Kubernetesyml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
    name: example-gateway
spec:
    gatewayClassName: example-gateway-class
    listeners:
        - name: http
          protocol: HTTP
          port: 80

3. HTTPRoute

Defines HTTP routing rules. Managed by application developers.

Kubernetesyml
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: 8080

Installing Gateway API

Step 1: Install Gateway API CRDs

Kubernetesbash
sudo kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml

Verify installation:

Kubernetesbash
sudo kubectl get crd | grep gateway

Output:

Kubernetesbash
gatewayclasses.gateway.networking.k8s.io
gateways.gateway.networking.k8s.io
httproutes.gateway.networking.k8s.io
referencegrants.gateway.networking.k8s.io

Step 2: Install Gateway Controller

For this example, we'll use NGINX Gateway Fabric:

Kubernetesbash
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.yaml

Verify controller:

Kubernetesbash
sudo kubectl get pods -n nginx-gateway

Creating Your First Gateway

Step 1: Create GatewayClass

Kubernetesgateway-class.yml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
    name: nginx
spec:
    controllerName: gateway.nginx.org/nginx-gateway-controller

Apply:

Kubernetesbash
sudo kubectl apply -f gateway-class.yml

Step 2: Create Gateway

Kubernetesgateway.yml
apiVersion: 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: Same

Apply:

Kubernetesbash
sudo kubectl apply -f gateway.yml

Verify:

Kubernetesbash
sudo kubectl get gateway

Output:

Kubernetesbash
NAME              CLASS   ADDRESS         READY   AGE
example-gateway   nginx   203.0.113.10    True    30s

Step 3: Create Application and Service

Kubernetesapp.yml
apiVersion: 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: 80

Apply:

Kubernetesbash
sudo kubectl apply -f app.yml

Step 4: Create HTTPRoute

Kuberneteshttp-route.yml
apiVersion: 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: 80

Apply:

Kubernetesbash
sudo kubectl apply -f http-route.yml

Verify:

Kubernetesbash
sudo kubectl get httproute

Now 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:

  1. Disable the built-in Traefik (because modifying its config directly will keep getting overridden when want to enable Gateway API feature)
sudo nvim /etc/rancher/k3s/config.yaml
  1. Restart K3s
bash
sudo systemctl restart k3s
  1. Setup kubeconfig so you don’t need to use sudo
Kubernetesbash
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
  1. Install Traefik via Helm
helm repo add traefik https://traefik.github.io/charts
helm repo update
  1. Create a Gateway
Kubernetesgateway.yml
apiVersion: 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-tls
  1. Deploy the application using Deployment, Service, and HTTPRoute
Kubernetesapp.yml
# ================================
# 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: 80

Test the connection

Linuxbash
curl -k --resolve web.example.com:443:10.10.10.4 https://web.example.com -v

Advanced Routing with Gateway API

Header-Based Routing

Route based on HTTP headers:

Kubernetesheader-route.yml
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: 8080

Query Parameter Routing

Route based on query parameters:

Kubernetesquery-route.yml
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: 8080

HTTP Method Routing

Route based on HTTP methods:

Kubernetesmethod-route.yml
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: 8080

Traffic Splitting and Canary Deployments

Gateway API makes traffic splitting simple:

Weighted Traffic Distribution

Kubernetescanary-route.yml
# ================================
# Alpha Service
# ================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: alpha-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: alpha-service
  template:
    metadata:
      labels:
        app: alpha-service
    spec:
      containers:
        - name: alpha-service
          image: ghcr.io/armandwipangestu/alpha-service:1.0.1
          ports:
            - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: alpha-service
spec:
  selector:
    app: alpha-service
  ports:
    - port: 80
      targetPort: 3000
---
# ================================
# Beta Service
# ================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: beta-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: beta-service
  template:
    metadata:
      labels:
        app: beta-service
    spec:
      containers:
        - name: beta-service
          image: ghcr.io/armandwipangestu/beta-service:1.0.1
          ports:
            - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: beta-service
spec:
  selector:
    app: beta-service
  ports:
    - port: 80
      targetPort: 3000
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: canary-gateway
spec:
  gatewayClassName: traefik
  listeners:
    - name: http
      protocol: HTTP
      port: 8000
      hostname: "*.example.com"
 
    - name: https
      protocol: HTTPS
      port: 8443
      hostname: "*.example.com"
      tls:
        mode: Terminate
        certificateRefs:
          - name: example-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
spec:
  parentRefs:
    - name: canary-gateway
      sectionName: https
    - name: canary-gateway
      sectionName: http
  hostnames:
    - "canary.example.com"
  rules:
    - backendRefs:
        - name: alpha-service
          port: 80
          weight: 90
        - name: beta-service
          port: 80
          weight: 10

This sends 90% of traffic to alpha version and 10% to beta version.

Progressive Canary Rollout

Kubernetesprogressive-canary.yml
# 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: 5

Update weights progressively: 5% → 25% → 50% → 100%

TLS Configuration

Configure HTTPS with Gateway API:

Step 1: Create TLS Secret

Kubernetesbash
sudo kubectl create secret tls example-tls \
    --cert=path/to/tls.crt \
    --key=path/to/tls.key

Step 2: Configure Gateway with TLS

Kubernetestls-gateway.yml
apiVersion: 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: Same

Step 3: Create HTTPRoute for HTTPS

Kuberneteshttps-route.yml
apiVersion: 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: 80

Cross-Namespace Routing

Gateway API supports routing across namespaces with ReferenceGrant:

Step 1: Create ReferenceGrant

Kubernetesreference-grant.yml
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: Service

Step 2: Create Cross-Namespace Route

Kubernetescross-ns-route.yml
apiVersion: 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: 8080

Request/Response Modification

Modify requests and responses:

Request Header Modification

Kubernetesheader-modification.yml
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: 8080

URL Rewrite

Kubernetesurl-rewrite.yml
apiVersion: 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: 8080

Request Mirroring

Mirror traffic to another service for testing:

Kubernetesmirror-route.yml
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: 8080

Traffic goes to production-service, but is also mirrored to test-service.

Timeout Configuration

Configure request timeouts:

Kubernetestimeout-route.yml
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: 8080

Practical Example: Complete Microservices Setup

Kubernetesmicroservices-gateway.yml
apiVersion: 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: 3000

Monitoring and Observability

Check Gateway Status

Kubernetesbash
sudo kubectl get gateway example-gateway -o yaml

Look for status conditions:

Kubernetesyml
status:
    conditions:
        - type: Accepted
          status: "True"
        - type: Programmed
          status: "True"
    addresses:
        - value: 203.0.113.10

Check HTTPRoute Status

Kubernetesbash
sudo kubectl get httproute web-route -o yaml

View Gateway Events

Kubernetesbash
sudo kubectl describe gateway example-gateway

Migration from Ingress to Gateway API

Before (Ingress)

Kubernetesyml
apiVersion: 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: 80

After (Gateway API)

Kubernetesyml
apiVersion: 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: 80

Best Practices

Use Role-Based Resources

Separate concerns by persona:

Kubernetesyml
# Infrastructure team manages GatewayClass
# Cluster operators manage Gateway
# Developers manage HTTPRoute

Leverage Traffic Splitting

Use for canary deployments:

Kubernetesyml
backendRefs:
    - name: stable
      weight: 95
    - name: canary
      weight: 5

Configure Timeouts

Always set appropriate timeouts:

Kubernetesyml
timeouts:
    request: 30s
    backendRequest: 25s

Use ReferenceGrant for Security

Control cross-namespace access:

Kubernetesyml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant

Monitor Gateway Health

Check status regularly:

Kubernetesbash
sudo kubectl get gateway -o wide
sudo kubectl get httproute -o wide

Use Descriptive Names

Choose clear resource names:

Kubernetesyml
# Good
name: api-gateway
name: user-service-route
 
# Avoid
name: gw1
name: route

Troubleshooting

Gateway Not Ready

Check controller logs:

Kubernetesbash
sudo kubectl logs -n nginx-gateway -l app=nginx-gateway

HTTPRoute Not Working

Verify parent reference:

Kubernetesbash
sudo kubectl get httproute <route-name> -o yaml

Check status conditions.

Cross-Namespace Issues

Verify ReferenceGrant:

Kubernetesbash
sudo kubectl get referencegrant -A

TLS Certificate Issues

Check secret exists:

Kubernetesbash
sudo kubectl get secret <tls-secret-name>

Conclusion

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 is the evolution of Kubernetes Ingress
  • Role-oriented design separates infrastructure, cluster, and app concerns
  • More expressive routing with headers, query params, methods
  • Traffic splitting built-in for canary deployments
  • Multi-protocol support (HTTP, HTTPS, TCP, UDP, gRPC)
  • Extensible with custom filters and resources
  • Portable across different implementations
  • Three main resources: GatewayClass, Gateway, HTTPRoute
  • Advanced features: request mirroring, URL rewrite, timeouts
  • ReferenceGrant for secure cross-namespace routing
  • Graduated to GA in 2023 for core features
  • Coexists with traditional Ingress (not a replacement)

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

Episode 20Episode 20

Related Posts