Experimenting with Gateway API using kind

Kubernetes Blog
January 28, 2026
This document will guide you through setting up a local experimental environment with Gateway API on kind. This setup is designed for learning and testing. It helps you understand Gateway API concepts without production complexity. Caution: This is an experimentation learning setup, and should not be used for production. The components used on this document are not suited for production usage. Once you're ready to deploy Gateway API in a production environment, select an implementation that suits your needs. Overview In this guide, you will: 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 This setup is ideal for learning, development, and experimentation with Gateway API concepts. Prerequisites Before you begin, ensure you have the following installed on your local machine: Docker - Required to run kind and cloud-provider-kind kubectl - The Kubernetes command-line tool kind - Kubernetes in Docker curl - Required to test the routes Create a kind cluster Create a new kind cluster by running: kind create cluster This will create a single-node Kubernetes cluster running in a Docker container. Install cloud-provider-kind Next, you need cloud-provider-kind, which provides two key components for this setup: A LoadBalancer controller that assigns addresses to LoadBalancer-type Services A Gateway API controller that implements the Gateway API specification It also automatically installs the Gateway API Custom Resource Definitions (CRDs) in your cluster. Run cloud-provider-kind as a Docker container on the same host where you created the kind cluster: 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 that cloud-provider-kind is running: docker ps --filter name=cloud-provider-kind You should see the container listed and in a running state. You can also check the logs: docker logs cloud-provider-kind Experimenting with Gateway API Now that your cluster is set up, you can start experimenting with Gateway API resources. cloud-provider-kind automatically provisions a GatewayClass called cloud-provider-kind. You'll use this class to create your Gateway. It is worth noticing that while kind is not a cloud provider, the project is named as cloud-provider-kind as it provides features that simulate a cloud-enabled environment. Deploy a Gateway The following manifest will: Create a new namespace called gateway-infra Deploy a Gateway that listens on port 80 Accept HTTPRoutes with hostnames matching the *.exampledomain.example pattern Allow routes from any namespace to attach to the Gateway. Note: In real clusters, prefer Same or Selector values on the allowedRoutes namespace selector field to limit attachments. Apply the following manifest: --- 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 Then verify that your Gateway is properly programmed and has an address assigned: 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 show True, and the ADDRESS field should contain an IP address. Deploy a demo application Next, deploy a simple echo application that will help you test your Gateway configuration. This application: Listens on port 3000 Echoes back request details including path, headers, and environment variables Runs in a namespace called demo Apply the following manifest: 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: - env: - name: POD_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.name - name: NAMESPACE valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.namespace image: registry.k8s.io/gateway-api/echo-basic:v20251204-v1.4.1 name: echo-basic Create an HTTPRoute Now create an HTTPRoute to route traffic from your Gateway to the echo application. This HTTPRoute will: Respond to requests for the hostname some.exampledomain.example Route traffic to the echo application Attach to the Gateway in the gateway-infra namespace Apply the following manifest: 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 Test your route The final step is to test your route using curl. You'll make a request to the Gateway's IP address with the hostname some.exampledomain.example. The command below is for POSIX shell only, and may need to be adjusted for your environment: 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 this: { "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 response, congratulations! Your Gateway API setup is working correctly. Troubleshooting If something isn't working as expected, you can troubleshoot by checking the status of your resources. Check the Gateway status First, inspect your Gateway resource: kubectl get gateway -n gateway-infra gateway -o yaml Look at the status section for conditions. Your Gateway should have: Accepted: True - The Gateway was accepted by the controller Programmed: True - The Gateway was successfully configured .status.addresses populated with an IP address Check the HTTPRoute status Next, inspect your HTTPRoute: kubectl get httproute -n demo echo -o yaml Check the status.parents section for conditions. Common issues include: ResolvedRefs set to False with reason BackendNotFound; this means that the backend Service doesn't exist or has the wrong name Accepted set to False; this means that the route couldn't attach to the Gateway (check namespace permissions or hostname matching) Example error when a backend is not found: 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 If the resource statuses don't reveal the issue, check the cloud-provider-kind logs: docker logs -f cloud-provider-kind This will show detailed logs from both the LoadBalancer and Gateway API controllers. Cleanup When you're finished with your experiments, you can clean up the resources: Remove Kubernetes resources Delete the namespaces (this will remove all resources within them): kubectl delete namespace gateway-infra kubectl delete namespace demo Stop cloud-provider-kind Stop and remove the cloud-provider-kind container: docker stop cloud-provider-kind Because the container was started with the --rm flag, it will be automatically removed when stopped. Delete the kind cluster Finally, delete the kind cluster: kind delete cluster Next steps Now that you've experimented with Gateway API locally, you're ready to explore production-ready implementations: Production Deployments: Review the Gateway API implementations to find a controller that matches your production requirements Learn More: Explore the Gateway API documentation to learn about advanced features like TLS, traffic splitting, and header manipulation Advanced Routing: Experiment with path-based routing, header matching, request mirroring and other features following Gateway API user guides A final word of caution This kind setup is for development and learning only. Always use a production-grade Gateway API implementation for real workloads.
Verticals
devopskubernetes
Originally published on Kubernetes Blog on 1/28/2026