How to Set Up a Local Kubernetes Cluster with kind
A complete walkthrough running a real multi-node Kubernetes cluster on your laptop with kind, including loading a locally-built image without pushing to a registry.
kind (“Kubernetes IN Docker”) runs a genuine, multi-node Kubernetes cluster locally, using Docker containers as the “nodes” — considerably lighter-weight than a full VM-based local cluster, and well suited to development and CI use.
Step 1: install kind and kubectl
brew install kind kubectl
(Or the equivalent for your platform — kind ships as a single static binary with no other dependencies beyond a working Docker installation.)
Step 2: create a basic single-node cluster
kind create cluster --name dev
This creates a complete Kubernetes control plane and a single worker node, both running as Docker containers, and automatically configures kubectl to point at it.
Step 3: verify it’s actually working
kubectl cluster-info --context kind-dev
kubectl get nodes
Step 4: create a more realistic multi-node cluster
For testing scheduling behavior, node affinity, or anything that genuinely needs more than one node to demonstrate:
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
kind create cluster --name dev --config kind-config.yaml
Step 5: build and load a local image without pushing anywhere
This is kind’s most practically useful feature for local development — testing a locally-built image without needing a registry at all:
docker build -t myapp:dev .
kind load docker-image myapp:dev --name dev
kind load docker-image copies the image directly into the cluster’s node containers, making it available to kubectl deployments without ever touching an external registry — genuinely useful for a fast local edit-build-test loop.
Step 6: deploy your application
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 2
selector:
matchLabels: {app: myapp}
template:
metadata:
labels: {app: myapp}
spec:
containers:
- name: myapp
image: myapp:dev
imagePullPolicy: Never
imagePullPolicy: Never is essential here — without it, Kubernetes will try to pull myapp:dev from a registry (where it doesn’t exist) rather than using the image you just loaded directly into the node.
kubectl apply -f deployment.yaml
Step 7: expose the service and reach it locally
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
type: NodePort
selector: {app: myapp}
ports:
- port: 80
targetPort: 8080
nodePort: 30080
kubectl apply -f service.yaml
For a cluster created with a port mapping configured in kind-config.yaml (via extraPortMappings), this NodePort becomes directly reachable on localhost; otherwise, kubectl port-forward reaches it without any extra configuration:
kubectl port-forward service/myapp 8080:80
Step 8: iterate quickly
The practical development loop from here is: change code, rebuild the image, kind load docker-image again, then restart the deployment to pick up the new image:
docker build -t myapp:dev . && kind load docker-image myapp:dev --name dev
kubectl rollout restart deployment/myapp
Step 9: tear down when done
kind delete cluster --name dev
Why kind instead of a full VM-based local cluster
Because kind’s nodes are Docker containers rather than full virtual machines, cluster creation and teardown take seconds rather than minutes, and resource overhead is dramatically lower — making it practical to spin up and discard clusters routinely as part of everyday development or CI, rather than treating a local Kubernetes cluster as a heavyweight, rarely-recreated fixture.