EthicsComputerCourses
Join training +91 90532 10052

The Gateway API

Kubernetes · Lesson 14 of 30 Intermediate 8 min read Updated

The Gateway API is the Kubernetes project's successor to Ingress. It does the same basic job, getting outside traffic to Services, but it was designed after years of Ingress experience: features such as redirects, header matching and traffic splitting are standard fields instead of controller-specific annotations, and the objects are split so that platform teams and app teams each own their part.

In this lesson you will install a Gateway API implementation on your kind cluster, create a Gateway and HTTPRoutes, and use the features that Ingress could only offer through annotations.

Three roles, three objects

ObjectUsually owned byWhat it describes
GatewayClassInfrastructure providerA kind of gateway and the controller that runs it, like IngressClass
GatewayCluster operatorAn actual entry point: which ports and protocols to listen on, TLS certificates, and which namespaces may attach routes
HTTPRouteApp developerRouting rules: host names, paths, headers, and which Services receive the traffic

The split matters in shared clusters. The operator controls the expensive, security-sensitive part (load balancer, certificates) once, and each team attaches its own routes without touching it. There are other route types too: GRPCRoute for gRPC services, and TLSRoute, TCPRoute and UDPRoute for non-HTTP traffic.

Installing it

The Gateway API is not built into Kubernetes. It has two pieces:

  • The CRDs (Custom Resource Definitions) that add the new object types to your cluster. They are versioned separately; v1.7.0 was released on 29 October 2026. The standard channel contains the stable objects; the experimental channel adds objects and fields that may still change.
  • An implementation: a controller plus a proxy, such as Envoy Gateway, Istio, Cilium, NGINX Gateway Fabric, Traefik, Kong or a cloud provider's own. The project's implementations page shows which ones pass its conformance tests.

Some implementations ask you to install the CRDs yourself:

Terminal
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.7.0/standard-install.yaml

This lesson uses Envoy Gateway, whose Helm chart installs the CRDs it needs together with its controller. Helm is a package manager for Kubernetes that you will study in Helm: Charts, Releases & Values; for now, install it (winget install Helm.Helm or brew install helm) and run:

Terminal
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.9.3 -n envoy-gateway-system --create-namespace
kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available

Check the Envoy Gateway documentation for its current version when you follow along.

GatewayClass and Gateway

YAML
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shop-gateway
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: Same

controllerName ties the class to Envoy Gateway. The Gateway asks for one HTTP listener on port 80, and allowedRoutes says that only routes in the Gateway's own namespace may attach (Same is the default; All and Selector open it to other namespaces). When Envoy Gateway sees this Gateway, it starts an Envoy proxy Deployment and a LoadBalancer Service for it.

An HTTPRoute

Priya's shop has the Services web and api from the Ingress lesson. This route sends /api to the API and everything else to the frontend:

YAML
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
spec:
  parentRefs:
    - name: shop-gateway
  hostnames:
    - shop.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: api
          port: 80
    - backendRefs:
        - name: web
          port: 80

parentRefs attaches the route to the Gateway. A rule without matches matches every path, and as with Ingress the most specific match wins.

Terminal
kubectl apply -f shop-gateway.yaml -f shop-route.yaml
kubectl get gateway shop-gateway
kubectl get httproute shop
Output
gatewayclass.gateway.networking.k8s.io/eg created
gateway.gateway.networking.k8s.io/shop-gateway created
httproute.gateway.networking.k8s.io/shop created
NAME           CLASS   ADDRESS      PROGRAMMED   AGE
shop-gateway   eg      172.18.0.6   True         45s
NAME   HOSTNAMES              AGE
shop   ["shop.example.com"]   30s

This output comes from a kind cluster with cloud-provider-kind running, which hands out addresses for LoadBalancer Services; on a cloud cluster you get a public address instead. Without a load balancer the Gateway has no ADDRESS, but you can still test it by port-forwarding to the Envoy Service that Envoy Gateway created (it lives in envoy-gateway-system and carries the label gateway.envoyproxy.io/owning-gateway-name=shop-gateway):

Terminal
ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system -l gateway.envoyproxy.io/owning-gateway-name=shop-gateway -o jsonpath='{.items[0].metadata.name}')
kubectl -n envoy-gateway-system port-forward service/$ENVOY_SERVICE 8888:80 &
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: shop.example.com" http://localhost:8888/api/books

If a route does not work, kubectl describe httproute shop shows its status conditions: Accepted tells you whether the Gateway accepted it, and ResolvedRefs whether its backend Services exist. This built-in feedback is a big improvement over Ingress.

Things Ingress needed annotations for

Redirect HTTP to HTTPS with a standard filter (on a route attached to the HTTP listener, while another listener serves HTTPS):

YAML
rules:
  - filters:
      - type: RequestRedirect
        requestRedirect:
          scheme: https
          statusCode: 301

Match on a header, for example to send testers to a beta version:

YAML
- matches:
    - headers:
        - name: x-beta-tester
          value: "yes"
  backendRefs:
    - name: web-beta
      port: 80

Split traffic by weight for a canary release: 90% of requests go to the stable version and 10% to the new one. Change the weights step by step as confidence grows:

YAML
- backendRefs:
    - name: web-v1
      port: 80
      weight: 90
    - name: web-v2
      port: 80
      weight: 10

Other standard filters rewrite URLs (URLRewrite), add or remove headers (RequestHeaderModifier, ResponseHeaderModifier) and copy traffic to a test backend (RequestMirror).

Across namespaces

In a shared cluster, the Gateway often lives in an infra namespace and each team's routes in their own namespace. Two safety locks control this:

  • The Gateway's allowedRoutes decides which namespaces may attach routes to it.
  • A route that sends traffic to a Service in another namespace needs a ReferenceGrant in that Service's namespace, explicitly allowing it. Without one, the route's ResolvedRefs condition reports the reference as not permitted.

Both locks stop one team from accidentally (or deliberately) hijacking another team's traffic.

Common mistakes

  • Applying routes before the CRDs exist. You get "no matches for kind HTTPRoute". Install the implementation (or the CRDs) first.
  • A parentRefs name or namespace that does not match the Gateway. The route stays unattached; check its Accepted condition.
  • Forgetting allowedRoutes or a ReferenceGrant when crossing namespaces.
  • Mixing CRD versions. Use the Gateway API version your implementation supports, and upgrade CRDs before upgrading the implementation when its notes say so.

Key points

  • Gateway API splits routing into GatewayClass (provider), Gateway (operator) and HTTPRoute or other routes (app teams).
  • It is installed as CRDs plus an implementation such as Envoy Gateway, Istio, Cilium or a cloud controller; v1.7.0 is the current release.
  • HTTPRoutes match on host, path, headers and more, and attach to a Gateway through parentRefs.
  • Redirects, rewrites, header changes, mirroring and weighted traffic splitting are standard features, not annotations.
  • allowedRoutes and ReferenceGrant control what may cross namespace boundaries; route status conditions show what went wrong.

Exercise

Write an HTTPRoute called results for host results.example.com, attached to shop-gateway, that sends / to the Service results-v1 (port 8080) for 80% of requests and to results-v2 (port 8080) for 20%, except that any request with the header x-team: qa always goes to results-v2.

Show solution
YAML
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: results
spec:
  parentRefs:
    - name: shop-gateway
  hostnames:
    - results.example.com
  rules:
    - matches:
        - headers:
            - name: x-team
              value: qa
      backendRefs:
        - name: results-v2
          port: 8080
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: results-v1
          port: 8080
          weight: 80
        - name: results-v2
          port: 8080
          weight: 20

A match without a path counts as the prefix /, so both rules match the same path. When paths tie, the rule with more header matches wins, so QA requests take the first rule. Everyone else is split 80/20 by weight.

Quick Quiz

1. Which Gateway API object does an app developer usually write?

2. How do you send 10% of traffic to a new version with the Gateway API?

3. An HTTPRoute in namespace team-a points at a Service in namespace team-b. What else is needed?

4. Why do you get "no matches for kind HTTPRoute" when applying a route?

Learn Kubernetes with a mentor

Free lessons get you started. Our Cloud & DevOps training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call