47

Kubernetes Gateway API: Ingress vs Gateway API

CKA prep • GatewayClass / Gateway / HTTPRoute, the role-oriented model, why it supersedes Ingress

Key terms

TermMeaning
Gateway APIThe GA successor to Ingress for L4/L7 routing
GatewayClassCluster resource naming the controller implementation (like IngressClass)
GatewayA request for an actual load balancer with listeners + ports
HTTPRouteHost/path/header routing rules attached to a Gateway
ListenerA port + protocol + hostname on a Gateway
Route attachmentHow a Route binds to a Gateway (via parentRefs + allowedRoutes)
Role-orientedSeparates infra/cluster/app personas into different resources

Problem & solution

Ingress is simple but cramped: it only does HTTP host/path routing, and every real feature (header routing, traffic splitting, TLS modes, gRPC) lives in vendor-specific annotations that do not port between controllers. There is also no clean split between the platform team that runs the load balancer and the app team that owns routes. Gateway API is the GA, expressive, portable, and role-oriented replacement.

Solution: Model routing as typed resources (GatewayClass, Gateway, HTTPRoute) split by persona, expressing host/path/header/weight routing in the spec instead of annotations, with a standard API that is portable across implementations.

The analogy

A modern port gate is split by role: the port authority installs the gate and owns the hardware and lanes, choosing a gate brand to build it, while each tenant writes only their own routing slips that say which trucks go to which pier through that shared gate. No tenant touches the gate itself, and the slips point at the destination piers. Gateway API splits routing the same way: the GatewayClass names the implementation, the Gateway is the operator-owned load balancer with its listeners, each HTTPRoute is an app team's own routing rules, and backendRefs point at the Services behind them.

The three core resources and their owners

Three personas, three resources — no shared annotation blob.

GatewayClass (the implementation)

Installed by the controller (Envoy Gateway, NGINX Gateway Fabric, Istio, Cilium, cloud providers). Analogous to IngressClass / a StorageClass.

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

Gateway (the load balancer + listeners)

A Gateway asks the chosen implementation for a real load balancer and declares the ports, protocols, and hostnames it listens on. This one opens an HTTP listener and a TLS-terminating HTTPS listener:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: web-gateway
  namespace: infra
spec:
  gatewayClassName: example-gateway-class
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: All          # which namespaces may attach routes
    - name: https
      protocol: HTTPS
      port: 443
      hostname: "*.example.com"
      tls:
        mode: Terminate
        certificateRefs:
          - name: example-tls

HTTPRoute (the routing rules)

A route attaches to a Gateway via parentRefs and can do path, header, and weighted backend routing directly in the spec.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
  namespace: store
spec:
  parentRefs:
    - name: web-gateway
      namespace: infra
  hostnames:
    - "shop.example.com"
  rules:
    - matches:
        - path: { type: PathPrefix, value: /cart }
      backendRefs:
        - name: cart
          port: 80
    - matches:
        - path: { type: PathPrefix, value: /api }
          headers:
            - name: x-canary
              value: "true"
      backendRefs:
        - name: api-canary
          port: 8080
          weight: 20
        - name: api
          port: 8080
          weight: 80

Ingress vs Gateway API

Gateway API was designed to fix Ingress's limits, so a direct comparison shows why it supersedes it:

   +----------------------+--------------------------+---------------------------+
   |                      | Ingress                  | Gateway API               |
   +----------------------+--------------------------+---------------------------+
   | scope                | HTTP host/path only      | HTTP, gRPC, TCP, TLS, UDP |
   | advanced routing     | vendor annotations       | header/weight in the spec |
   | portability          | low (annotation lock-in) | high (standard API)       |
   | personas             | one resource             | GatewayClass/Gateway/Route|
   | cross-namespace       | awkward                  | first-class + ReferenceGrant|
   | status               | stable but frozen        | GA, actively extended     |
   +----------------------+--------------------------+---------------------------+

Install and inspect

Gateway API ships as CRDs plus a controller; they are not built into core Kubernetes yet.

# install the standard-channel CRDs, then a controller of your choice
kubectl apply -f \
  https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml

kubectl get gatewayclass
kubectl get gateway -A
kubectl get httproute -A
kubectl describe gateway web-gateway -n infra   # check Programmed/Accepted conditions

Cross-namespace routing with ReferenceGrant

Routes in one namespace may target backends in another only if a ReferenceGrant in the backend's namespace allows it — explicit, auditable trust instead of annotation guesswork.

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-store-to-payments
  namespace: payments
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      namespace: store
  to:
    - group: ""
      kind: Service

End-to-end: a request through the Gateway API

End-to-end example: one Gateway, two HTTPRoutes, host and path split

A full role-oriented setup: the platform team installs a GatewayClass and a TLS-terminating Gateway; two app teams attach HTTPRoutes that split traffic by host and by path to different backend Services.

  1. Platform: install the CRDs, then define the GatewayClass and Gateway:
kubectl apply -f \
  https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
kubectl create namespace infra
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: prod-class
spec:
  controllerName: example.net/gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: web-gateway
  namespace: infra
spec:
  gatewayClassName: prod-class
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      hostname: "*.example.com"
      tls:
        mode: Terminate
        certificateRefs:
          - name: example-tls
      allowedRoutes:
        namespaces:
          from: All
  1. App team A: an HTTPRoute for shop.example.com that splits by path:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
  namespace: store
spec:
  parentRefs:
    - name: web-gateway
      namespace: infra
  hostnames:
    - "shop.example.com"
  rules:
    - matches:
        - path: { type: PathPrefix, value: /cart }
      backendRefs:
        - name: cart
          port: 80
    - matches:
        - path: { type: PathPrefix, value: /checkout }
      backendRefs:
        - name: checkout
          port: 80
  1. App team B: a second HTTPRoute on a different host, api.example.com:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api
  namespace: backend
spec:
  parentRefs:
    - name: web-gateway
      namespace: infra
  hostnames:
    - "api.example.com"
  rules:
    - matches:
        - path: { type: PathPrefix, value: / }
      backendRefs:
        - name: api
          port: 8080
  1. Apply everything and confirm the Gateway is Programmed and both routes Accepted:
kubectl apply -f gatewayclass.yaml -f gateway.yaml -f shop-route.yaml -f api-route.yaml
kubectl -n infra get gateway web-gateway \
  -o jsonpath='{.status.conditions[?(@.type=="Programmed")].status}{"\n"}'   # True
kubectl -n store get httproute shop \
  -o jsonpath='{.status.parents[0].conditions[?(@.type=="Accepted")].status}{"\n"}'  # True
kubectl -n backend get httproute api \
  -o jsonpath='{.status.parents[0].conditions[?(@.type=="Accepted")].status}{"\n"}'  # True
  1. Test both hosts against the Gateway address without changing DNS:
GW=$(kubectl -n infra get gateway web-gateway -o jsonpath='{.status.addresses[0].value}')
curl -k --resolve shop.example.com:443:$GW https://shop.example.com/cart
# expected: response from the cart Service
curl -k --resolve api.example.com:443:$GW https://api.example.com/v1/health
# expected: response from the api Service

Common pitfalls

These are the most common Gateway API mistakes and what each symptom points to:

   - CRDs not installed         -> kind Gateway/HTTPRoute unknown to the API
   - GatewayClass has no ctrl   -> Gateway stays not Programmed (nothing implements it)
   - route won't attach         -> allowedRoutes.namespaces or parentRefs mismatch
   - cross-namespace backend    -> missing ReferenceGrant in the backend namespace
   - expecting Ingress annot.    -> use spec fields (matches/weight), not annotations
   - wrong apiVersion           -> v1 for Gateway/HTTPRoute, v1beta1 for ReferenceGrant

Key takeaways

  • Gateway API is the GA successor to Ingress: richer, portable, role-oriented.
  • Three resources: GatewayClass (impl), Gateway (LB+listeners), HTTPRoute (rules).
  • Advanced routing (headers, weighted splits, TLS modes) lives in the spec, not annotations.
  • It ships as CRDs + a controller, not in core Kubernetes (yet).
  • allowedRoutes + ReferenceGrant make cross-namespace routing explicit and safe.
  • Check Accepted/Programmed conditions to confirm a Gateway is wired up.

Checklist

  • [ ] Installed the Gateway API CRDs and a controller
  • [ ] Created a GatewayClass and a Gateway with HTTP + HTTPS listeners
  • [ ] Wrote an HTTPRoute with path + header matches and a weighted split
  • [ ] Allowed a route from another namespace with a ReferenceGrant
  • [ ] Contrasted Ingress annotations vs Gateway API spec fields
  • [ ] Verified Programmed/Accepted status conditions