How to Run Kubernetes in Docker (kind)
Create local Kubernetes clusters using kind (Kubernetes in Docker). Set up multi-node clusters, configure networking, and test applications locally.
💡 Quick Answer: Install kind (
brew install kind), then create a cluster withkind create cluster --name my-cluster. For multi-node: use a config file withnodes:section specifying control-plane and workers. Kind uses Docker containers as nodes—perfect for local development and CI/CD.Key command:
kind create cluster --config kind-config.yaml; delete withkind delete cluster --name my-cluster.Gotcha: kind clusters don’t persist across Docker restarts by default; use
extraMountsfor persistent volumes andextraPortMappingsfor ingress.
kind (Kubernetes IN Docker) runs Kubernetes clusters using Docker containers as nodes. It’s perfect for local development, testing, and CI/CD pipelines.
Install kind
# macOS with Homebrew
brew install kind
# Linux
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
# Windows with Chocolatey
choco install kind
# Verify installation
kind versionCreate Basic Cluster
# Create cluster with default name "kind"
kind create cluster
# Create cluster with custom name
kind create cluster --name my-cluster
# Create with specific Kubernetes version
kind create cluster --image kindest/node:v1.28.0
# List clusters
kind get clusters
# Get cluster info
kubectl cluster-info --context kind-my-clusterMulti-Node Cluster
# multi-node-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
- role: workerkind create cluster --config multi-node-config.yaml
kubectl get nodesHigh Availability Cluster
# ha-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: control-plane
- role: control-plane
- role: worker
- role: workerkind create cluster --name ha-cluster --config ha-config.yamlExpose Ports (Ingress)
# ingress-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
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# Create cluster with ingress support
kind create cluster --config ingress-config.yaml
# Install NGINX Ingress Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
# Wait for ingress controller
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=90sNodePort Access
# nodeport-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000
hostPort: 30000
- containerPort: 30001
hostPort: 30001# Create NodePort service accessible on localhost:30000
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --type=NodePort --port=80 --node-port=30000
curl localhost:30000Mount Host Directory
# mount-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraMounts:
- hostPath: /path/on/host
containerPath: /data
readOnly: false
- role: worker
extraMounts:
- hostPath: /path/on/host
containerPath: /dataLoad Docker Image
# Build local image
docker build -t myapp:v1 .
# Load image into kind cluster
kind load docker-image myapp:v1 --name my-cluster
# For images from archive
kind load image-archive myimage.tar --name my-cluster
# Verify image is available
docker exec -it my-cluster-control-plane crictl images | grep myappCustom Kubernetes Configuration
# custom-k8s-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
# Enable specific feature gates
CSIMigration: true
runtimeConfig:
# Enable specific API versions
"api/alpha": "true"
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
apiServer:
extraArgs:
enable-admission-plugins: NodeRestriction,PodSecurityPolicy
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
max-pods: "150"Networking Configuration
# networking-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
# Disable default CNI (install your own)
disableDefaultCNI: true
# Change pod subnet
podSubnet: "10.244.0.0/16"
# Change service subnet
serviceSubnet: "10.96.0.0/12"
# API server address
apiServerAddress: "127.0.0.1"
# API server port
apiServerPort: 6443Install Custom CNI
# calico-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
disableDefaultCNI: true
podSubnet: "192.168.0.0/16"
nodes:
- role: control-plane
- role: worker# Create cluster
kind create cluster --config calico-config.yaml
# Install Calico
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
# Wait for Calico
kubectl wait --for=condition=ready pod -l k8s-app=calico-node -n kube-system --timeout=90sLocal Registry
#!/bin/bash
# create-cluster-with-registry.sh
# Create registry container
reg_name='kind-registry'
reg_port='5001'
if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
docker run -d --restart=always -p "127.0.0.1:${reg_port}:5000" --name "${reg_name}" registry:2
fi
# Create kind cluster with registry config
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"]
endpoint = ["http://${reg_name}:5000"]
EOF
# Connect registry to kind network
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
docker network connect "kind" "${reg_name}"
fi
# Document the local registry
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: local-registry-hosting
namespace: kube-public
data:
localRegistryHosting.v1: |
host: "localhost:${reg_port}"
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
EOF# Use the local registry
docker tag myapp:v1 localhost:5001/myapp:v1
docker push localhost:5001/myapp:v1
# Deploy using local registry
kubectl create deployment myapp --image=localhost:5001/myapp:v1Delete Cluster
# Delete specific cluster
kind delete cluster --name my-cluster
# Delete default cluster
kind delete cluster
# Delete all clusters
kind delete clusters --allCI/CD Usage
# GitHub Actions example
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create kind cluster
uses: helm/kind-action@v1.8.0
with:
cluster_name: test-cluster
- name: Test cluster
run: |
kubectl cluster-info
kubectl get nodes
- name: Run tests
run: |
kubectl apply -f manifests/
kubectl wait --for=condition=ready pod -l app=myapp --timeout=60sTroubleshooting
# Get cluster logs
kind export logs --name my-cluster ./logs
# Check Docker containers
docker ps -a | grep kind
# Access node shell
docker exec -it my-cluster-control-plane bash
# Check kubelet logs on node
docker exec -it my-cluster-control-plane journalctl -u kubelet
# Restart cluster (delete and recreate)
kind delete cluster --name my-cluster
kind create cluster --name my-cluster --config config.yamlCommon Issues
# Port already in use
# Check what's using the port
lsof -i :80
# Change port mapping in config
# Image not found
# Load image into cluster
kind load docker-image myapp:v1
# Network issues
# Restart Docker
docker restart
# Insufficient resources
# Increase Docker memory/CPU limits
# Docker Desktop > Settings > ResourcesSummary
kind creates disposable Kubernetes clusters in Docker containers for local development and testing. Use configuration files to set up multi-node clusters, expose ports for ingress, and mount host directories. Load local Docker images with kind load docker-image. kind is ideal for CI/CD pipelines due to its fast creation time and isolation. Remember to delete clusters when done to free resources.
📘 Go Further with Kubernetes Recipes
Love this recipe? There’s so much more! This is just one of 100+ hands-on recipes in our comprehensive Kubernetes Recipes book.
Inside the book, you’ll master:
- ✅ Production-ready deployment strategies
- ✅ Advanced networking and security patterns
- ✅ Observability, monitoring, and troubleshooting
- ✅ Real-world best practices from industry experts
“The practical, recipe-based approach made complex Kubernetes concepts finally click for me.”
👉 Get Your Copy Now — Start building production-grade Kubernetes skills today!

Recommended
Kubernetes Recipes — The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book →Learn by Doing
CopyPasteLearn — Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses →