Overview
Kind (Kubernetes IN Docker) is a tool for running local Kubernetes clusters using Docker containers as nodes. It’s perfect for local development, testing, and CI/CD pipelines.
Why Kind?
Fast Setup Create a cluster in seconds without heavy virtualization
Multi-Node Support Simulate production topologies with control-plane and worker nodes
Port Mapping Expose services on your localhost for easy testing
CI/CD Ready Lightweight and perfect for automated testing pipelines
Prerequisites
Before you begin, install:
Docker Desktop (or Docker Engine)
Kind CLI
# macOS
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 (using Chocolatey)
choco install kind
kubectl
# macOS
brew install kubectl
# Linux
curl -LO "https://dl.k8s.io/release/$( curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
Cluster Configuration
Basic Cluster Setup
Create a simple single-node cluster:
kind create cluster --name local
This creates a basic cluster with one control-plane node.
Multi-Node Cluster with Port Mapping
For a production-like environment with port forwarding, create a cluster configuration file:
kind : Cluster
apiVersion : kind.x-k8s.io/v1alpha4
nodes :
- role : control-plane
extraPortMappings :
- containerPort : 30007 # need this as kind runs on docker
hostPort : 30007
- containerPort : 30008
hostPort : 30008
- role : worker
- role : worker
Port Mapping Explained:
containerPort: Port inside the Kind container
hostPort: Port exposed on your localhost
Maps NodePort services to your local machine for easy access
Create the cluster with this configuration:
kind create cluster --name local --config cluster-config.yaml
This creates:
1 control-plane node with port mappings
2 worker nodes
Services accessible on localhost:30007 and localhost:30008
Cluster Management
List Clusters
View all Kind clusters:
Get Cluster Info
Check cluster details:
kubectl cluster-info --context kind-local
Switch Context
Kind automatically sets the kubectl context. To switch back:
kubectl config use-context kind-local
View all contexts:
kubectl config get-contexts
Delete Cluster
Remove a cluster when done:
kind delete cluster --name local
Working with Your Cluster
Verify Nodes
Check that all nodes are ready:
Expected output:
NAME STATUS ROLES AGE VERSION
local-control-plane Ready control-plane 2m v1.27.3
local-worker Ready <none> 2m v1.27.3
local-worker2 Ready <none> 2m v1.27.3
Deploy a Test Application
Create a simple nginx deployment:
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort --name=nginx-service
Get the NodePort:
kubectl get service nginx-service
Access Services Locally
With port mapping configured, access NodePort services directly:
# If your service uses NodePort 30007
curl http://localhost:30007
Alternatively, use port-forwarding for any service:
kubectl port-forward service/nginx-service 8080:80
Access at http://localhost:8080
Loading Local Images
One of Kind’s most powerful features is loading local Docker images without a registry.
Build and Load an Image
# Build your image
docker build -t my-app:local .
# Load it into Kind
kind load docker-image my-app:local --name local
Deploy Using Local Image
apiVersion : apps/v1
kind : Deployment
metadata :
name : my-app
spec :
replicas : 2
selector :
matchLabels :
app : my-app
template :
metadata :
labels :
app : my-app
spec :
containers :
- name : my-app
image : my-app:local
imagePullPolicy : Never # Important: Don't try to pull from registry
ports :
- containerPort : 8080
Always set imagePullPolicy: Never when using locally loaded images to prevent Kubernetes from trying to pull them from a registry.
Apply the deployment:
kubectl apply -f deployment.yaml
Advanced Configuration
Custom Kubernetes Version
Specify a Kubernetes version:
kind create cluster --name local --image kindest/node:v1.27.3
Ingress Support
Enable ingress for testing ingress controllers:
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 and install NGINX ingress:
kind create cluster --config ingress-cluster.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
Persistent Storage
Kind supports persistent volumes using local storage:
apiVersion : v1
kind : PersistentVolumeClaim
metadata :
name : local-storage
spec :
accessModes :
- ReadWriteOnce
resources :
requests :
storage : 1Gi
Development Workflow
Typical Development Loop
Code changes → Make changes to your application
Build image → docker build -t my-app:local .
Load image → kind load docker-image my-app:local --name local
Restart pods → kubectl rollout restart deployment/my-app
Test → Access via port-forward or NodePort
Debug → kubectl logs -f deployment/my-app
Hot Reload with Skaffold
For faster iteration, use Skaffold with Kind:
# Install Skaffold
brew install skaffold
# Run in dev mode
skaffold dev
Skaffold automatically rebuilds and redeploys on file changes.
Testing Production Scenarios
Simulate Node Failures
# Get node name
kubectl get nodes
# Drain a node (simulates maintenance)
kubectl drain local-worker --ignore-daemonsets --delete-emptydir-data
# Watch pods reschedule
kubectl get pods -o wide -w
# Uncordon the node
kubectl uncordon local-worker
Test Resource Constraints
Apply resource limits to simulate constrained environments:
apiVersion : v1
kind : Pod
metadata :
name : resource-test
spec :
containers :
- name : app
image : nginx
resources :
requests :
memory : "64Mi"
cpu : "250m"
limits :
memory : "128Mi"
cpu : "500m"
Troubleshooting
Cluster Won’t Start
Check Docker is running:
Increase Docker resources (Docker Desktop → Settings → Resources):
CPU: At least 2 cores
Memory: At least 4GB
Image Pull Errors
If pods can’t pull images:
# Verify image is loaded
docker exec -it local-control-plane crictl images
# Reload if needed
kind load docker-image my-app:local --name local
Port Already in Use
If ports 30007/30008 are taken:
# Find what's using the port
lsof -i :30007
# Kill the process or use different ports in cluster-config.yaml
Reset Everything
Clean slate:
kind delete cluster --name local
docker system prune -a
kind create cluster --name local --config cluster-config.yaml
Optimize Kind for Development:
Keep clusters small (1-2 worker nodes unless testing scaling)
Delete unused images: docker image prune -a
Use lightweight base images (Alpine Linux)
Disable unnecessary features in development
Restart Docker Desktop if performance degrades
Comparing to Production (GKE)
Feature Kind (Local) GKE (Production) Cost Free Paid Setup Time Seconds Minutes Multi-node Simulated True distributed Load Balancers Port mapping Real LBs Persistent Storage Host volumes Cloud disks Scaling Manual Auto-scaling Best For Development Production
Next Steps
Deploy to GKE Move your app to production on GKE
ArgoCD GitOps Set up GitOps workflow
Additional Resources