Skip to main content

kind: Local Kubernetes in Docker

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:

FlagPurposeExample
--nameCluster namekind create cluster --name prod
--configPath to YAML--config ./kind.yaml
--imageCustom node image--image kindest/node:v1.28.0
--waitReadiness timeout--wait 5m
--kubeconfigAlternative kubeconfig--kubeconfig ~/.kube/dev
Tip

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.