Getting Started with Vanilla (Kind)
Kind (Kubernetes in Docker) runs upstream Kubernetes clusters using Docker containers as nodes. It provides a lightweight, fast way to run standard Kubernetes locally without requiring a VM. This guide shows you how to use Vanilla (Kind) with KSail for local development, testing, and learning.
What is Vanilla (Kind)?
Section titled βWhat is Vanilla (Kind)?βVanilla is KSailβs name for the upstream Kubernetes distribution running via Kind. It provides:
- Standard Kubernetes: Unmodified upstream K8s with full compatibility
- Fast cluster creation: 30-60 seconds for a working cluster
- Docker-only: Runs entirely in Docker containers (no VM overhead)
- Multi-node support: Simulate production topologies locally
- Native configuration: Uses standard
kind.yamlfiles (no lock-in)
When to Use Vanilla
Section titled βWhen to Use VanillaβVanilla (Kind) is ideal for:
- Learning Kubernetes: Upstream K8s without distribution-specific modifications
- Application development: Fast local iterative development with full K8s API compatibility
- CI/CD testing: Ephemeral clusters for integration tests and validation
- GitOps workflows: Test Flux/ArgoCD configurations locally before production
- Multi-node testing: Simulate production topologies (control plane + workers)
- Kubernetes contributors: Test upstream features and changes
When NOT to Use Vanilla
Section titled βWhen NOT to Use VanillaβConsider alternatives if you need:
- Minimal resource usage: K3s is lighter (single-binary, fewer components)
- Built-in LoadBalancer: K3s has ServiceLB; Vanilla requires Cloud Provider KIND
- Virtual clusters: VCluster provides isolation without separate nodes
- Production deployments: Talos offers immutable infrastructure and enhanced security
- Embedded storage: K3s includes local-path-provisioner; Vanilla requires explicit CSI
Quick Start
Section titled βQuick StartβCreate your first Vanilla cluster in under 60 seconds.
Prerequisites
Section titled βPrerequisitesβ- Docker Desktop or Docker Engine installed and running
docker pscommand works
Step 1: Initialize Project
Section titled βStep 1: Initialize Projectβksail cluster init \ --name my-cluster \ --distribution Vanilla \ --control-planes 1 \ --workers 2This creates:
ksail.yamlβ KSail configurationkind.yamlβ Kind-specific cluster configuration
Step 2: Create Cluster
Section titled βStep 2: Create Clusterβksail cluster createKSail will:
- Generate Kind configuration from
ksail.yaml - Create Docker containers as Kubernetes nodes
- Install Kubernetes components
- Configure kubectl context
- Install CNI (Cilium by default)
Expected output:
β Creating Vanilla cluster with Kind...β Installing Cilium CNI...β Cluster ready!
Cluster: my-clusterNodes: 3 (1 control plane, 2 workers)Provider: DockerStep 3: Verify Cluster
Section titled βStep 3: Verify Clusterβ# Check cluster infoksail cluster info
# View nodeskubectl get nodes
# Expected output:# NAME STATUS ROLES AGE VERSION# my-cluster-control-plane Ready control-plane 2m v1.31.0# my-cluster-worker Ready <none> 2m v1.31.0# my-cluster-worker2 Ready <none> 2m v1.31.0Step 4: Deploy Sample Application
Section titled βStep 4: Deploy Sample Applicationβ# Create a deploymentkubectl create deployment nginx --image=nginx
# Expose as ClusterIP servicekubectl expose deployment nginx --port=80
# Verify deploymentkubectl get podsStep 5: Cleanup
Section titled βStep 5: Cleanupβ# Delete cluster and all resourcesksail cluster deleteConfiguration
Section titled βConfigurationβBasic Configuration
Section titled βBasic ConfigurationβThe ksail.yaml file controls your cluster:
# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.jsonapiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: distribution: Vanilla distributionConfig: kind.yamlKind-Specific Configuration
Section titled βKind-Specific ConfigurationβCustomize Kind behavior in kind.yaml:
kind: ClusterapiVersion: kind.x-k8s.io/v1alpha4nodes: - role: control-plane # Port mapping for accessing services extraPortMappings: - containerPort: 80 hostPort: 8080 protocol: TCP - role: worker - role: worker
# Containerd configurationcontainerdConfigPatches: - |- [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5000"] endpoint = ["http://localhost:5000"]LoadBalancer Support
Section titled βLoadBalancer SupportβEnable LoadBalancer services with Cloud Provider KIND:
apiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: distribution: Vanilla loadBalancer: EnabledWhen enabled, KSail installs Cloud Provider KIND, which:
- Creates
cpk-*Docker containers as LoadBalancer proxies - Assigns external IPs to LoadBalancer services
- Automatically cleans up on cluster deletion
Example LoadBalancer service:
# Create LoadBalancer servicekubectl expose deployment nginx --type=LoadBalancer --port=80
# Get external IPkubectl get svc nginx
# Access from hostcurl http://<EXTERNAL-IP>Registry Mirrors
Section titled βRegistry MirrorsβConfigure registry mirrors to avoid rate limits. KSail enables docker.io, ghcr.io, quay.io, and registry.k8s.io mirrors by default. Override with --mirror-registry flags:
ksail cluster init --name my-cluster --distribution Vanilla \ --mirror-registry 'docker.io=https://registry-1.docker.io' \ --mirror-registry 'ghcr.io=https://ghcr.io' \ --mirror-registry 'quay.io=https://quay.io' \ --mirror-registry 'registry.k8s.io=https://registry.k8s.io'KSail injects containerd registry configuration into all Kind nodes.
Common Use Cases
Section titled βCommon Use Casesβ1. Local Application Development
Section titled β1. Local Application DevelopmentβScenario: Develop a microservices application with hot-reload workflows.
# Initialize development clusterksail cluster init --name dev --distribution Vanilla --workers 1
# Create clusterksail cluster create
# Deploy applicationkubectl apply -f k8s/
# Watch for changes and redeploykubectl rollout restart deployment/my-appWhy Vanilla: Fast cluster creation, standard K8s API, compatible with production manifests.
2. CI/CD Integration Testing
Section titled β2. CI/CD Integration TestingβScenario: Run integration tests in ephemeral Kubernetes clusters.
# CI pipeline scriptksail cluster init --name ci-test --distribution Vanillaksail cluster create
# Run testskubectl apply -f test-manifests/./run-integration-tests.sh
# Cleanupksail cluster deleteWhy Vanilla: Predictable environment, fast creation/deletion, standard Kubernetes.
3. GitOps Testing (Flux/ArgoCD)
Section titled β3. GitOps Testing (Flux/ArgoCD)βScenario: Test GitOps configurations locally before production deployment.
# Initialize with GitOpsksail cluster init \ --name gitops-test \ --distribution Vanilla \ --gitops-engine Flux
# Create and bootstrap Fluxksail cluster createksail workload apply
# Test manifests reconciliationkubectl get kustomizations -AWhy Vanilla: Full compatibility with production GitOps workflows, standard K8s APIs.
4. Multi-Node Topology Testing
Section titled β4. Multi-Node Topology TestingβScenario: Test pod scheduling, node affinity, and multi-node features.
# Create 3-node clusterksail cluster init \ --name topology-test \ --distribution Vanilla \ --control-planes 1 \ --workers 3
ksail cluster create
# Test node affinitykubectl label nodes topology-test-worker zone=us-east-1akubectl label nodes topology-test-worker2 zone=us-east-1b
# Deploy with affinity ruleskubectl apply -f deployment-with-affinity.yamlWhy Vanilla: Simulates multi-node production environments, supports node labels and taints.
Architecture
Section titled βArchitectureβCluster Components
Section titled βCluster Componentsβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Docker Host ββ ββ βββββββββββββββββββββββββββββββββββββββββββββββββββββ ββ β Control Plane Node (Docker Container) β ββ β β’ kube-apiserver β ββ β β’ kube-controller-manager β ββ β β’ kube-scheduler β ββ β β’ etcd β ββ β β’ kubelet β ββ β β’ containerd (container runtime) β ββ βββββββββββββββββββββββββββββββββββββββββββββββββββββ ββ ββ ββββββββββββββββββββ ββββββββββββββββββββ ββ β Worker Node 1 β β Worker Node 2 β ββ β β’ kubelet β β β’ kubelet β ββ β β’ containerd β β β’ containerd β ββ β β’ kube-proxy β β β’ kube-proxy β ββ β β’ CNI (Cilium) β β β’ CNI (Cilium) β ββ ββββββββββββββββββββ ββββββββββββββββββββ ββ ββ βββββββββββββββββββββββββββββββββββββββββββββββββββββ ββ β Optional: Cloud Provider KIND (cpk- containers) β ββ β β’ LoadBalancer proxy containers β ββ βββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββHow It Works
Section titled βHow It Worksβ- Node Creation: Kind creates Docker containers running systemd
- Kubernetes Install: Standard upstream K8s components installed via kubeadm
- Networking: Bridge network connects all nodes
- Storage: Each node has ephemeral storage (lost on cluster delete)
- LoadBalancer: Optional Cloud Provider KIND creates proxy containers
KSail Integration
Section titled βKSail Integrationβ- Provisioner:
pkg/svc/provisioner/cluster/kind/wraps Kind SDK - Infrastructure: Docker provider manages container lifecycle (start/stop)
- Configuration: Generates
kind.yamlfromksail.yamldeclaratively - Installers: CNI (Cilium), CSI (local-path-storage), metrics-server, Cloud Provider KIND
Distribution Comparison
Section titled βDistribution Comparisonβ| Feature | Vanilla (Kind) | K3s (K3d) | Talos | VCluster |
|---|---|---|---|---|
| Kubernetes Type | Upstream | Lightweight | Upstream | Virtual |
| Resource Usage | Medium | Low | Medium | Very Low |
| Startup Time | 30-60s | 15-30s | 60-90s | 10-20s |
| LoadBalancer | Cloud Provider KIND | ServiceLB (built-in) | MetalLB/hcloud-ccm | Host cluster LB |
| Storage | local-path (optional) | local-path (built-in) | Hetzner CSI | Host cluster storage |
| Production Ready | β Local only | β Local only | β Docker + Cloud | β Virtual only |
| Multi-Node | β Yes | β Yes | β Yes | β Single pod |
| Shell Access | β Docker exec | β Docker exec | β No shell | β kubectl exec |
| GitOps Support | β Full | β Full | β Full | β Full |
| Best For | Learning, Testing | Quick dev, CI/CD | Production, Security | Multi-tenancy, CI |
| API Compatibility | 100% Upstream | 99% Compatible | 100% Upstream | 100% Upstream |
| Node Isolation | Full | Full | Full | Virtual |
| Cluster Lifecycle | Create/Delete | Create/Delete | Create/Delete/Update | Create/Delete |
Troubleshooting
Section titled βTroubleshootingβ1. Cluster Creation Fails
Section titled β1. Cluster Creation FailsβSymptom: ksail cluster create fails with Docker errors.
Solutions:
# Check Docker is runningdocker ps
# Check available disk spacedf -h
# Clean up unused containers, networks, and dangling images (safer)docker system prune
# OPTIONAL (more aggressive):# This will remove ALL unused images (not just dangling ones) and may affect other projects.# Use only if the above prune is not sufficient and you understand the impact.# docker system prune -a
# Retry cluster creationksail cluster create2. Nodes Not Ready
Section titled β2. Nodes Not ReadyβSymptom: kubectl get nodes shows NotReady status.
Solutions:
# Check node conditionskubectl describe node <node-name>
# Check CNI podskubectl get pods -n kube-system | grep cilium
# Restart CNI if neededkubectl rollout restart daemonset/cilium -n kube-system3. LoadBalancer Stuck in Pending
Section titled β3. LoadBalancer Stuck in PendingβSymptom: LoadBalancer service never gets EXTERNAL-IP.
Solutions:
# Check Cloud Provider KIND is enabledkubectl get pods -A | grep cloud-provider-kind
# Verify LoadBalancer enabled in ksail.yamlcat ksail.yaml | grep -A2 loadBalancer
# Enable LoadBalancer if missing# Edit ksail.yaml and set spec.cluster.loadBalancer: Enabled
# Update clusterksail cluster update4. Port Already in Use
Section titled β4. Port Already in UseβSymptom: address already in use error during cluster creation.
Solutions:
# Check existing Kind clusterskind get clusters
# Delete conflicting clusterkind delete cluster --name <conflicting-cluster>
# Or use different nameksail cluster init --name my-cluster-25. Out of Memory or Disk Space
Section titled β5. Out of Memory or Disk SpaceβSymptom: Cluster creation fails with resource errors.
Solutions:
# Use fewer nodes (single-node cluster)ksail cluster init --name my-cluster --distribution Vanilla --control-planes 1 --workers 0
# Increase Docker resources (Docker Desktop)# Settings β Resources β Adjust CPU/Memory/Disk
# Clean up Dockerdocker system prune -a --volumesAdvanced Topics
Section titled βAdvanced TopicsβMulti-Node Clusters
Section titled βMulti-Node ClustersβCreate production-like topologies:
apiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: distribution: Vanillaksail cluster init \ --name multi-node \ --distribution Vanilla \ --control-planes 3 \ --workers 5Kubernetes Version Selection
Section titled βKubernetes Version SelectionβKind determines the Kubernetes version via the node image tag. To use a specific version, configure the node image in your kind.yaml:
kind: ClusterapiVersion: kind.x-k8s.io/v1alpha4nodes: - role: control-plane image: kindest/node:v1.31.0 - role: worker image: kindest/node:v1.31.0Supported versions: Check Kind release notes for image availability.
Custom CNI
Section titled βCustom CNIβOverride default CNI:
ksail cluster init --name my-cluster --distribution Vanilla --cni CalicoAvailable CNI options: Default, Cilium, Calico.
Port Mappings
Section titled βPort MappingsβExpose services on host ports:
nodes: - role: control-plane extraPortMappings: - containerPort: 30080 # NodePort service hostPort: 8080 protocol: TCPAccess services at http://localhost:8080.
Storage Configuration
Section titled βStorage ConfigurationβEnable persistent storage:
ksail cluster init --name my-cluster --distribution Vanilla --csi EnabledCreate PersistentVolumeClaims:
kubectl apply -f - <<EOFapiVersion: v1kind: PersistentVolumeClaimmetadata: name: my-pvcspec: accessModes: - ReadWriteOnce resources: requests: storage: 1GiEOFCluster Lifecycle Management
Section titled βCluster Lifecycle Managementβ# Stop cluster (preserves state)ksail cluster stop
# Start stopped clusterksail cluster start
# Update cluster (may require recreation)ksail cluster update
# List all clustersksail cluster list
# Get cluster infoksail cluster infoIntegration with Native Kind
Section titled βIntegration with Native KindβKSail generates standard kind.yaml files:
# Use Kind CLI directlykind create cluster --config kind.yaml
# Export kubeconfig for an existing Kind clusterkind export kubeconfig --name my-clusterNo vendor lock-inβconfigurations are portable, and KSail can interact with any Kind cluster accessible via your kubeconfig.
Next Steps
Section titled βNext StepsβExplore KSail Features
Section titled βExplore KSail Featuresβ- GitOps Integration: Bootstrap Flux or ArgoCD
- Secret Management: Encrypt secrets with SOPS
- AI Chat: Interactive cluster management with GitHub Copilot
- VSCode Extension: Manage clusters from your editor
Try Other Distributions
Section titled βTry Other Distributionsβ- K3s (K3d): Lightweight K3s for resource-constrained environments and CI/CD
- Talos: Immutable infrastructure for production
- VCluster: Virtual clusters for multi-tenancy
Learn More
Section titled βLearn Moreβ- Installation Guide: Install KSail on your system
- CLI Flags: Comprehensive command documentation
- Configuration Reference: Detailed YAML options
- LoadBalancer Configuration: LoadBalancer options for Vanilla
- Contributing: Contribute to KSail development