You do not need cloud servers to learn Kubernetes. kind (Kubernetes in Docker) runs each cluster node as a Docker container and starts a conformant cluster in about a minute. It is also widely used in CI to test manifests and Helm charts on every commit.
Versions in use on this date. kind 0.10, Docker 20.10, and kubectl 1.21 (released 8 April 2021). kind publishes node images per Kubernetes version; this post pins the 1.20 image, which kind 0.10 supports, and uses the newer kubectl, which is allowed because kubectl may be one minor version ahead of or behind the cluster. Check the kind release notes for the exact image tag and digest of each release, and pin it.
What kind creates
flowchart TB
DEV[Your laptop with Docker] --> KC[kind CLI]
KC --> C1
subgraph DOCKER[Docker containers acting as nodes]
C1[dev-control-plane<br/>API server etcd scheduler]
W1[dev-worker]
W2[dev-worker2]
end
KC -->|writes kubeconfig| KUBE[~/.kube/config context kind-dev]
KUBECTL[kubectl] --> KUBE
KUBECTL -->|6443 mapped to localhost| C1
C1 --- W1
C1 --- W2
HP[localhost 80 and 443] -->|extraPortMappings| C1
IMG[Local docker images] -->|kind load docker-image| DOCKER
Each node container runs systemd, containerd and kubelet, so what you learn matches a real cluster. The CNI is a simple built-in (kindnet) by default.
Install
# kind (Linux amd64); check the releases page for the latest 0.10.x
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.10.0/kind-linux-amd64
chmod +x kind && sudo mv kind /usr/local/bin/
# kubectl
curl -LO "https://dl.k8s.io/release/v1.21.0/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kind version; kubectl version --client --short; docker info --format '{{.ServerVersion}}'
Give Docker enough resources: 4 CPUs and 6 to 8 GB RAM is comfortable for a three-node cluster; on Docker Desktop, set this in preferences.
A cluster you can actually use
kind-config.yaml:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: dev
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- { containerPort: 80, hostPort: 80, protocol: TCP }
- { containerPort: 443, hostPort: 443, protocol: TCP }
- role: worker
- role: worker
kind create cluster --config kind-config.yaml
kubectl cluster-info --context kind-dev
kubectl get nodes -o wide
docker ps --format '{{.Names}}\t{{.Image}}'
The ingress-ready label and port mappings prepare the control-plane node to host an ingress controller reachable on localhost (covered in the ingress post). Delete and recreate clusters freely; they are disposable.
Run something
kubectl create deployment hello --image=nginx:1.20 --replicas=2
kubectl expose deployment hello --port=80
kubectl get pods -o wide # spread across the workers
kubectl port-forward svc/hello 8080:80 &
curl -I localhost:8080
Your own images without a registry
kind nodes cannot see your host’s Docker images, so load them:
docker build -t myapp:dev .
kind load docker-image myapp:dev --name dev
kubectl create deployment myapp --image=myapp:dev
kubectl set image deployment/myapp myapp=myapp:dev
Use a tag other than latest, or set imagePullPolicy: IfNotPresent; with latest Kubernetes tries to pull from a registry and fails.
Useful commands
kind get clusters
kind export logs ./kind-logs --name dev # debug bundle
kubectl config get-contexts; kubectl config use-context kind-dev
kind delete cluster --name dev
Use in CI
# GitHub Actions
- uses: engineerd/setup-kind@v0.5.0
with: { version: v0.10.0 }
- run: |
kubectl apply -f k8s/
kubectl wait --for=condition=Available deployment/myapp --timeout=120s
Tests then run against a real API server: manifest validation, Helm installs, controller behaviour.
Limits
- Not for production or performance testing; everything shares your machine.
- LoadBalancer services stay
<pending>without an add-on such as MetalLB; use NodePort, port-forward, or ingress. - Persistent data lives inside node containers and vanishes when the cluster is deleted.
- kind exercises Kubernetes itself, not your cloud’s load balancers, IAM integration or storage classes.
For comparison: Minikube and k3d offer similar local clusters; kind’s strengths are speed, multi-node support and CI use.