Learning Kubernetes on a shared dev cluster is slow and slightly frightening: every experiment is visible, and a mistake is someone else’s afternoon. A local cluster removes both problems. It starts in seconds, it’s free, and deleting it when you’ve broken it is the intended workflow.

Pick one

ToolWhat it isBest for
kindKubernetes running in Docker containersCI, and matching a specific cluster version
k3dK3s (a light distribution) in DockerThe fastest start and the smallest footprint
minikubeA VM or container with add-onsLearning, and the built-in ingress/dashboard add-ons

Any of the three is fine. kind is the one CI pipelines standardise on, because a cluster in a GitHub Actions job is one command.

  brew install kind
kind create cluster --name dev
kubectl cluster-info --context kind-dev
kind delete cluster --name dev
  

A config that maps ports and pins the version — the two things you’ll want immediately:

  # kind.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    image: kindest/node:v1.34.0
    extraPortMappings:
      - containerPort: 30080
        hostPort: 8080
  - role: worker
  
  kind create cluster --config kind.yaml
kind load docker-image myapp:dev        # skip a registry entirely
  

kind load is the trick worth knowing: build an image locally and push it straight into the cluster’s nodes, no registry involved.

The inner loop is the real problem

Even locally, edit → build → push → apply → wait → look is far too slow to iterate. Two tools collapse it:

  brew install tilt
tilt up            # watches files, rebuilds, redeploys, streams logs in one UI
  
  # Tiltfile
docker_build('myapp', '.', live_update=[sync('./src', '/app/src')])
k8s_yaml(kustomize('k8s/overlays/dev'))
k8s_resource('myapp', port_forwards='8080:8080')
  

live_update is the part that changes the feel: for an interpreted language it syncs the changed files into the running container instead of rebuilding the image, so a save is visible in about a second. Skaffold does the same job with a YAML config and fits better where the team already has Google tooling.

Use it in CI too

  - uses: helm/kind-action@v1
  with:
    cluster_name: test
- run: |
    kubectl apply -k k8s/overlays/test
    kubectl wait --for=condition=available deploy/myapp --timeout=120s
    ./scripts/smoke-test.sh
  

That’s a genuine integration test of your manifests — including that the Deployment becomes ready, which is the failure a kubectl apply --dry-run will never catch.

What a local cluster won’t tell you

It has no cloud load balancer, no real storage classes, no node autoscaling, and none of your production network policy. It proves the manifests are coherent and the app starts; it does not prove the thing will survive Tuesday. Keep a staging environment for that.

Next

Whichever cluster you’re pointed at, these are the tools for looking inside → kubectl and k9s

Last updated 25 Aug 2026, 00:00 UTC. history