kind creates a Kubernetes cluster from Docker containers: control-plane and worker nodes are kindest/node images. Primary use cases are local development and CI. GitHub Actions has an official create-kind action that makes pipelines with K8s tests straightforward.
Compared to minikube, kind has no hypervisor dependency and natively supports multi-node topologies. Compared to k3d, it requires no Rancher and talks to CRI/containerd directly.
Installation
macOS, Linux, and Windows (via WSL2) — download the binary from GitHub Releases.
# macOS
brew install kind
# Linux
curl -Lo /usr/local/bin/kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64
chmod +x /usr/local/bin/kind
# Verify
kind version
# kind v0.22.0 go1.21.8 linux/amd64
Docker is required (or Podman with kind use docker driver). Make sure Docker has at least 4 GB of memory allocated for all containers.
First Cluster in One Command
kind create cluster
# Creating cluster "kind" ...
# ✓ Ensuring node image (kindest/node:v1.29.0) ✓
# ✓ Preparing nodes ✓
# ✓ Writing configuration ✓
# ✓ Starting control-plane ✓
# ✓ Installing CNI ✓
# ✓ Installing StorageClass ✓
# ✓ Waiting for node readiness ✓
# Successfully created cluster "kind"!
kind created a cluster named kind and wrote kubeconfig to ~/.kube/config. Verify:
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# kind-control-plane Ready control-plane 2m v1.29.0
kubectl get pods -A
# NAMESPACE NAME READY
# kube-system coredns-... 1/1
# local-path-storage local-path-provisioner-... 1/1
Done. Single-node cluster is up in a minute.
Config: Multi-node and Runtime
For a cluster with multiple worker nodes, use a YAML config. Let’s create three workers:
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80
hostPort: 8080
protocol: TCP
- role: worker
- role: worker
- role: worker
kind create cluster --name multi --config kind-config.yaml
Cluster creation flags:
| Flag | Purpose | Example |
|---|---|---|
--name | Cluster name | kind create cluster --name prod |
--config | Path to YAML | --config ./kind.yaml |
--image | Custom node image | --image kindest/node:v1.28.0 |
--wait | Readiness timeout | --wait 5m |
--kubeconfig | Alternative kubeconfig | --kubeconfig ~/.kube/dev |
Instead of --image, you can set node.internalImage in the config — useful for air-gapped environments.
Loading Images into the Cluster
kind uses a separate container runtime inside the node. Images from your local Docker daemon are not visible. To load them:
# Build the image
docker build -t myapp:v1.0 ./myapp
# Load into kind nodes
kind load docker-image myapp:v1.0 --name multi
# For specific nodes
kind load docker-image myapp:v1.1 --name multi --nodes kind-worker,kind-worker2
For CI, you often load from a tar archive:
docker save myapp:v1.0 > myapp.tar
kind load image-archive myapp.tar --name multi
After loading, the image is available in the cluster without a registry.
kind with kubeconfig
By default, kind merges the context into ~/.kube/config. For isolation:
# Separate kubeconfig
KUBECONFIG=~/.kube/kind-config kind create cluster --name isolated
# Or export after creation
kind get kubeconfig --name multi > ./kubeconfig
export KUBECONFIG=./kubeconfig
kubectl get nodes
Managing multiple clusters:
kind get clusters
# kind
# multi
# isolated
# Delete specific one
kind delete cluster --name isolated
Cleanup
kind delete cluster --name multi
# Deleting cluster "multi" ...
Without --name, the default cluster (kind) is deleted. All Docker resources are removed with the nodes. If Docker was stopped while the cluster was running, nodes remain in NotReady on the next start. Fix by recreating the cluster.
Gotchas
containerd inside the node. kubectl talks to containerd directly. Familiar docker ps and docker exec won’t show pods — they live inside the kind node. For debugging:
docker exec -it multi-control-plane crictl ps
docker exec -it multi-control-plane crictl logs <container-id>
HostNetworking and PortMappings. For external access to pods, you need extraPortMappings in the config. Without it, hostPort does not work — pod networking in kind is isolated.
PersistentVolumes. kind creates a kind-node StorageClass backed by local-path-provisioner. Data lives on the host at /var/local-path-provisioner. For a clean environment, just delete the cluster.
cgroups v2. In newer distros (Ubuntu 22.04+, Fedora), Docker may require configuration:
docker info | grep cgroup
# Cgroup Driver: systemd
# Cgroup Version: 2
kind works with both versions, but rare issues with memory limits occur with cgroups v2 on Arch Linux. Solution — pass in the config:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
"MemoryManager": true
runtimeConfig:
"memorymanager.k8s.io/v1alpha1": true
Versions. kind lags behind upstream Kubernetes by 1-2 minor versions. Check the compatibility matrix in the project README before setting up a prod-like environment.
kind is a fast way to spin up Kubernetes on a developer machine or in CI without virtualization. Main loop: kind create cluster, work, kind delete cluster. For air-gapped or multi-node scenarios — YAML config and kind load docker-image. Known limitations: no GPU support, no real network stack, performance below bare metal. For everything else — it works.