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.
In this guide, you will:
This setup is ideal for learning, development, and experimentation with Gateway API concepts.
Before you begin, ensure you have the following installed on your local machine:
Create a new kind cluster by running:
kind create cluster
This will create a single-node Kubernetes cluster running in a Docker container.
Next, you need cloud-provider-kind, which provides two key components for this setup:
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
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.
The following manifest will:
gateway-infra*.exampledomain.example patternallowedRoutes 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.
Next, deploy a simple echo application that will help you test your Gateway configuration. This application:
demoApply 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
Now create an HTTPRoute to route traffic from your Gateway to the echo application. This HTTPRoute will:
some.exampledomain.examplegateway-infra namespaceApply 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
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.
If something isn't working as expected, you can troubleshoot by checking the status of your resources.
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 controllerProgrammed: True - The Gateway was successfully configured.status.addresses populated with an IP addressNext, inspect your HTTPRoute:
kubectl get httproute -n demo echo -o yaml
Check the status.parents section for conditions. Common issues include:
BackendNotFound; this means that the backend Service doesn't exist or has the wrong nameExample 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
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.
When you're finished with your experiments, you can clean up the resources:
Delete the namespaces (this will remove all resources within them):
kubectl delete namespace gateway-infra
kubectl delete namespace demo
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.
Finally, delete the kind cluster:
kind delete cluster
Now that you've experimented with Gateway API locally, you're ready to explore production-ready implementations:
This kind setup is for development and learning only. Always use a production-grade Gateway API implementation for real workloads.