Skip to content
Back to blog Gateway API Advanced Patterns: Beyond Basic Ingress

Gateway API Advanced Patterns: Beyond Basic Ingress

K8sNetworking

Gateway API Advanced Patterns: Beyond Basic Ingress

Gateway API is the successor to Ingress. It separates concerns between infrastructure operators, cluster operators, and app developers. This guide covers advanced patterns you can’t do with traditional Ingress.

TL;DR

  • Gateway API = role-based ingress configuration
  • Traffic splitting for canary deployments
  • Header-based routing for A/B testing
  • Cross-namespace references with ReferenceGrants
  • TLS passthrough for end-to-end encryption

Role Separation

ROLE                    RESOURCE                RESPONSIBILITY
====                    ========                ==============
Infrastructure Admin    GatewayClass            Define infrastructure
Cluster Operator        Gateway                 Deploy/configure gateways
App Developer           HTTPRoute/TCPRoute      Define routing rules
┌─────────────────────────────────────────────────────────────────┐
│                    GatewayClass (Infra Admin)                    │
│                 "Which controller? What features?"               │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                    Gateway (Cluster Operator)                    │
│                 "Which ports? What TLS? Which IPs?"              │
└─────────────────────────────────────────────────────────────────┘

          ┌────────────────────┼────────────────────┐
          ▼                    ▼                    ▼
┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│   HTTPRoute      │  │   HTTPRoute      │  │   TCPRoute       │
│   (Team A)       │  │   (Team B)       │  │   (Team C)       │
└──────────────────┘  └──────────────────┘  └──────────────────┘

Install Gateway API

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

# Install experimental features (TCPRoute, TLSRoute)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/experimental-install.yaml

Basic Setup

# GatewayClass - defines the controller
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: production
spec:
  controllerName: gateway.nginx.org/nginx-gateway-controller  # or cilium, istio, etc.

---
# Gateway - the actual load balancer
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: gateway-system
spec:
  gatewayClassName: production
  listeners:
    - name: http
      port: 80
      protocol: HTTP
      hostname: "*.company.com"
      allowedRoutes:
        namespaces:
          from: All
    
    - name: https
      port: 443
      protocol: HTTPS
      hostname: "*.company.com"
      tls:
        mode: Terminate
        certificateRefs:
          - name: wildcard-tls
      allowedRoutes:
        namespaces:
          from: All

Traffic Splitting (Canary)

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-canary
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-system
  hostnames:
    - "api.company.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: api-stable
          port: 8080
          weight: 90
        - name: api-canary
          port: 8080
          weight: 10

Progressive rollout:

# Start: 100% stable
weight: 100 / 0

# Phase 1: 10% canary
weight: 90 / 10

# Phase 2: 50% canary
weight: 50 / 50

# Phase 3: 100% canary
weight: 0 / 100

# Promote: rename canary to stable

Header-Based Routing

Route based on headers for A/B testing or feature flags:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: feature-routing
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-system
  hostnames:
    - "app.company.com"
  rules:
    # Beta users (header-based)
    - matches:
        - headers:
            - name: X-Beta-User
              value: "true"
      backendRefs:
        - name: app-beta
          port: 8080
    
    # Internal testing (header-based)
    - matches:
        - headers:
            - name: X-Internal
              value: "true"
            - name: X-Test-Group
              value: "experiment-123"
      backendRefs:
        - name: app-experiment
          port: 8080
    
    # Default
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: app-stable
          port: 8080

Query Parameter Routing

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: query-routing
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-system
  rules:
    # Route by version query param
    - matches:
        - queryParams:
            - name: version
              value: v2
      backendRefs:
        - name: api-v2
          port: 8080
    
    # Route by debug flag
    - matches:
        - queryParams:
            - name: debug
              value: "true"
      backendRefs:
        - name: api-debug
          port: 8080

Method-Based Routing

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: method-routing
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-system
  hostnames:
    - "api.company.com"
  rules:
    # Reads go to replicas
    - matches:
        - method: GET
          path:
            type: PathPrefix
            value: /api/
      backendRefs:
        - name: api-read-replica
          port: 8080
    
    # Writes go to primary
    - matches:
        - method: POST
          path:
            type: PathPrefix
            value: /api/
        - method: PUT
          path:
            type: PathPrefix
            value: /api/
        - method: DELETE
          path:
            type: PathPrefix
            value: /api/
      backendRefs:
        - name: api-primary
          port: 8080

Cross-Namespace References

Allow routes in one namespace to reference services in another:

# In the service namespace (backend)
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-frontend-namespace
  namespace: backend
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      namespace: frontend
  to:
    - group: ""
      kind: Service

---
# In the frontend namespace
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: frontend-route
  namespace: frontend
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-system
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api/
      backendRefs:
        - name: api-service
          namespace: backend  # Cross-namespace reference
          port: 8080

TLS Passthrough

For end-to-end encryption where the gateway doesn’t terminate TLS:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: passthrough-gateway
spec:
  gatewayClassName: production
  listeners:
    - name: tls-passthrough
      port: 443
      protocol: TLS
      hostname: "secure.company.com"
      tls:
        mode: Passthrough
      allowedRoutes:
        kinds:
          - kind: TLSRoute

---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
  name: secure-passthrough
spec:
  parentRefs:
    - name: passthrough-gateway
  hostnames:
    - "secure.company.com"
  rules:
    - backendRefs:
        - name: secure-backend
          port: 8443

Request/Response Modification

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-modification
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-system
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      filters:
        # Add request headers
        - type: RequestHeaderModifier
          requestHeaderModifier:
            add:
              - name: X-Request-ID
                value: "${request_id}"
              - name: X-Forwarded-Proto
                value: https
            set:
              - name: Host
                value: internal-api.default.svc.cluster.local
            remove:
              - X-Internal-Token
        
        # Add response headers
        - type: ResponseHeaderModifier
          responseHeaderModifier:
            add:
              - name: X-Frame-Options
                value: DENY
              - name: X-Content-Type-Options
                value: nosniff
      
      backendRefs:
        - name: api
          port: 8080

URL Rewriting

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: url-rewrite
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-system
  hostnames:
    - "api.company.com"
  rules:
    # Rewrite /v1/* to /*
    - matches:
        - path:
            type: PathPrefix
            value: /v1/
      filters:
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /
      backendRefs:
        - name: api-v1
          port: 8080
    
    # Rewrite /legacy/* to /api/v0/*
    - matches:
        - path:
            type: PathPrefix
            value: /legacy/
      filters:
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /api/v0/
      backendRefs:
        - name: legacy-api
          port: 8080

Redirects

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: redirects
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-system
  rules:
    # HTTP to HTTPS redirect
    - matches:
        - path:
            type: PathPrefix
            value: /
      filters:
        - type: RequestRedirect
          requestRedirect:
            scheme: https
            statusCode: 301
    
    # Domain redirect
    - matches:
        - headers:
            - name: Host
              value: old.company.com
      filters:
        - type: RequestRedirect
          requestRedirect:
            hostname: new.company.com
            statusCode: 301

Timeouts and Retries

Using BackendRef extensions:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: with-timeouts
spec:
  parentRefs:
    - name: production-gateway
      namespace: gateway-system
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api/
      timeouts:
        request: 30s
        backendRequest: 10s
      backendRefs:
        - name: api
          port: 8080

TCP/UDP Routes

# TCP Route for databases
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
  name: postgres-route
spec:
  parentRefs:
    - name: tcp-gateway
  rules:
    - backendRefs:
        - name: postgres
          port: 5432

---
# Gateway with TCP listener
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: tcp-gateway
spec:
  gatewayClassName: production
  listeners:
    - name: postgres
      port: 5432
      protocol: TCP
      allowedRoutes:
        kinds:
          - kind: TCPRoute

Multi-Gateway Setup

# Internal gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: internal-gateway
  namespace: gateway-system
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-internal: "true"
spec:
  gatewayClassName: internal
  listeners:
    - name: http
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: internal

---
# External gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: external-gateway
  namespace: gateway-system
spec:
  gatewayClassName: external
  listeners:
    - name: https
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - name: wildcard-tls
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: external

Troubleshooting

# Check Gateway status
kubectl get gateway -A

# Check HTTPRoute status
kubectl get httproute -A -o wide

# Check attached routes
kubectl describe gateway production-gateway

# Check if routes are accepted
kubectl get httproute my-route -o jsonpath='{.status.parents}'

References

======================================== Gateway API + Kubernetes

The future of ingress. Role-based. Powerful.

Found this helpful?

Comments