Experimenting with Gateway API using kind

Published: (January 27, 2026 at 07:00 PM EST)
4 min read

Source: Kubernetes Blog

Overview

This guide will walk you through setting up a local experimental environment with Gateway API on kind. It’s intended for learning and testing, not for production use.

What you’ll do

  • Set up a local Kubernetes cluster using kind (Kubernetes in Docker)
  • Deploy cloud-provider-kind, which provides both LoadBalancer Services and a Gateway API controller
  • Create a Gateway and HTTPRoute to route traffic to a demo application
  • Test your Gateway API configuration locally

Prerequisites

Ensure the following tools are installed on your machine:

  • Docker – required to run kind and cloud‑provider‑kind
  • kubectl – Kubernetes command‑line tool
  • kind – Kubernetes in Docker
  • curl – needed to test the routes

Create a kind cluster

Create a single‑node cluster:

kind create cluster

Install cloud-provider-kind

cloud‑provider‑kind supplies two key components:

  1. A LoadBalancer controller that assigns addresses to LoadBalancer‑type Services
  2. A Gateway API controller that implements the Gateway API specification

It also automatically installs the Gateway API CRDs.

Run it as a Docker container on the same host:

VERSION="$(basename $(curl -s -L -o /dev/null -w '%{url_effective}' https://github.com/kubernetes-sigs/cloud-provider-kind/releases/latest))"
docker run -d --name cloud-provider-kind --rm --network host \
  -v /var/run/docker.sock:/var/run/docker.sock \
  registry.k8s.io/cloud-provider-kind/cloud-controller-manager:${VERSION}

Note: On some systems you may need elevated privileges to access the Docker socket.

Verify it’s running:

docker ps --filter name=cloud-provider-kind
docker logs cloud-provider-kind

Experimenting with Gateway API

cloud‑provider‑kind automatically creates a GatewayClass named cloud-provider-kind. You’ll use this class to create your Gateway.

Deploy a Gateway

The following manifest will:

  • Create a namespace gateway-infra
  • Deploy a Gateway listening on port 80
  • Accept HTTPRoutes with hostnames matching *.exampledomain.example
  • Allow routes from any namespace to attach to the Gateway
---
apiVersion: v1
kind: Namespace
metadata:
  name: gateway-infra
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: gateway
  namespace: gateway-infra
spec:
  gatewayClassName: cloud-provider-kind
  listeners:
  - name: default
    hostname: "*.exampledomain.example"
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: All

Apply the manifest:

kubectl apply -f gateway.yaml   # (or pipe the manifest directly)

Verify the Gateway is programmed and has an address:

kubectl get gateway -n gateway-infra gateway

Expected output

NAME     CLASS                 ADDRESS        PROGRAMMED   AGE
gateway  cloud-provider-kind   172.18.0.3     True         5m6s

The PROGRAMMED column should be True, and the ADDRESS field should contain an IP.

Deploy a demo application

Deploy a simple echo app that listens on port 3000 and returns request details.

apiVersion: v1
kind: Namespace
metadata:
  name: demo
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: echo
  name: echo
  namespace: demo
spec:
  ports:
  - name: http
    port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app.kubernetes.io/name: echo
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: echo
  name: echo
  namespace: demo
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: echo
  template:
    metadata:
      labels:
        app.kubernetes.io/name: echo
    spec:
      containers:
      - image: registry.k8s.io/gateway-api/echo-basic:v20251204-v1.4.1
        name: echo-basic
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace

Apply the manifest:

kubectl apply -f demo-app.yaml

Create an HTTPRoute

Route traffic from the Gateway to the echo app.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo
  namespace: demo
spec:
  parentRefs:
  - name: gateway
    namespace: gateway-infra
  hostnames: ["some.exampledomain.example"]
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: echo
      port: 3000

Apply the manifest:

kubectl apply -f httproute.yaml

Test your route

Fetch the Gateway’s IP address and issue a request with the appropriate hostname:

GW_ADDR=$(kubectl get gateway -n gateway-infra gateway -o jsonpath='{.status.addresses[0].value}')
curl --resolve some.exampledomain.example:80:${GW_ADDR} http://some.exampledomain.example

You should receive a JSON response similar to:

{
  "path": "/",
  "host": "some.exampledomain.example",
  "method": "GET",
  "proto": "HTTP/1.1",
  "headers": {
    "Accept": ["*/*"],
    "User-Agent": ["curl/8.15.0"]
  },
  "namespace": "demo",
  "ingress": "",
  "service": "",
  "pod": "echo-dc48d7cf8-vs2df"
}

If you see this output, your Gateway API setup is working.

Troubleshooting

Check the Gateway status

kubectl get gateway -n gateway-infra gateway -o yaml

Look for conditions:

  • Accepted: True
  • Programmed: True
  • .status.addresses populated with an IP

Check the HTTPRoute status

kubectl get httproute -n demo echo -o yaml

Inspect status.parents for conditions such as:

  • ResolvedRefs: False (reason: BackendNotFound) – the Service may be missing or misnamed
  • Accepted: False – the route couldn’t attach to the Gateway (check namespace permissions or hostname)

Example error snippet:

status:
  parents:
  - conditions:
    - lastTransitionTime: "2026-01-19T17:13:35Z"
      message: backend not found
      observedGeneration: 2
      reason: BackendNotFound
      status: "False"
      type: ResolvedRefs
    controllerName: kind.sigs.k8s.io/gateway-controller

Check controller logs

docker logs -f cloud-provider-kind

Logs include details from both the LoadBalancer and Gateway API controllers.

Cleanup

When you’re done experimenting:

Remove Kubernetes resources

kubectl delete namespace gateway-infra
kubectl delete namespace demo

Stop cloud‑provider‑kind

docker stop cloud-provider-kind   # container auto‑removes due to --rm

Delete the kind cluster

kind delete cluster

Next steps

  • Production Deployments: Review Gateway API implementations to find a controller that meets your production requirements.
  • Learn More: Explore the Gateway API docs for advanced features like TLS, traffic splitting, and header manipulation.
  • Advanced Routing: Try path‑based routing, header matching, request mirroring, and other capabilities using the official user guides.

A final word of caution

This kind‑based setup is for development and learning only. For real workloads, always use a production‑grade Gateway API implementation.

Back to Blog

Related posts

Read more »