Skip to content
daniel@cosenza:~/blog
SRE & DevOpsHow-To September 4, 2025 3 min read

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.