Skip to content

Gateway API

The Kubernetes Gateway API replaces the Ingress resource, implemented by Cilium for both load balancing and TLS termination. The improvement is the split in ownership: the cluster owns the Gateway — addresses, certificates, ports — and each app owns its own HTTPRoute.

At a glance

Namespace gateway-system for the Application, kube-system for the Gateways themselves
Stage 01-crds for the CRDs, 07-ingress for the Gateways
Depends on Cilium to implement it, cert-manager for the wildcard certificates
If it is down Nothing reaches any hostname. Running pods keep running
Health check kubectl -n kube-system get gateway → both PROGRAMMED=True with an address
Files payload/platform/gateway-api/, payload/platform/gateway-api-crds/

Configuration

Two Gateways in kube-system, each with a dedicated address from the Cilium L2 pool, defined in gateways.yaml:

Gateway Hostname pattern Used for
apps-gateway *.k8s.wlkr.ch User-facing workloads
infra-gateway *.infra.k8s.wlkr.ch Platform services (Grafana, Hubble, etc.)

Both terminate TLS with cert-manager's wildcard certificates, so a new hostname needs no certificate of its own and nothing to renew. Port 80 is accepted from all namespaces only so the central rule in http-redirect.yaml can send it to HTTPS.

Usage

Create an HTTPRoute in the app's namespace referencing the right Gateway; external-dns creates the record and the wildcard covers the name:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app
  namespace: my-app
spec:
  parentRefs:
    - name: apps-gateway
      namespace: kube-system
      sectionName: https
  hostnames:
    - "my-app.k8s.wlkr.ch"
  rules:
    - backendRefs:
        - name: my-app-svc
          port: 80

Use infra-gateway with a *.infra.k8s.wlkr.ch hostname for platform tools.

Health check

Check that each HTTP listener carries only the redirect — http should show exactly 1:

kubectl get gateway infra-gateway -n kube-system \
  -o jsonpath='{range .status.listeners[*]}{.name}={.attachedRoutes}{"\n"}{end}'

Pitfalls

Always set sectionName: https

A route that names no listener attaches to both, and on port 80 it beats the redirect: Gateway API resolves competing routes by hostname specificity, so a route naming a hostname wins over the catch-all redirect and serves the app in cleartext. Nothing reports it — every route is Accepted.

Nothing stops two apps claiming the same hostname

Both Gateways admit routes from every namespace, so a stray HTTPRoute can attach itself to infra-gateway and claim a name — see Security Posture.