Skip to content

Getting Started with K3s (K3d)

K3s is a lightweight, certified Kubernetes distribution designed for resource-constrained environments. KSail uses K3d to run K3s clusters in Docker containers, giving you fast cluster creation with batteries-included defaults like a built-in load balancer, local storage provisioner, and metrics server.

K3s is a minimal Kubernetes distribution that bundles everything into a single binary. K3d wraps K3s in Docker containers so you can run full clusters locally. It offers a minimal footprint, batteries-included defaults (ServiceLB, local-path-provisioner, metrics-server), fast 15–30 s cluster creation, and standard k3d.yaml files with no lock-in.

K3s (K3d) is ideal for quick local development, resource-constrained machines or CI runners, CI/CD pipelines with ephemeral clusters, and multi-node topology testing.

Create your first K3s cluster in under 30 seconds.

  • Docker Desktop or Docker Engine installed and running
  • docker ps command works
Terminal window
ksail cluster init \
--name my-cluster \
--distribution K3s \
--control-planes 1 \
--workers 2

This creates:

  • ksail.yaml — KSail configuration
  • k3d.yaml — K3d-specific cluster configuration
Terminal window
ksail cluster create
Terminal window
# Check cluster info
ksail cluster info
# View nodes
kubectl get nodes
# Expected output:
# NAME STATUS ROLES AGE VERSION
# k3d-my-cluster-server-0 Ready control-plane,master 2m v1.31.x+k3s1
# k3d-my-cluster-agent-0 Ready <none> 2m v1.31.x+k3s1
# k3d-my-cluster-agent-1 Ready <none> 2m v1.31.x+k3s1
Terminal window
# Create a deployment
kubectl create deployment nginx --image=nginx
# Expose as LoadBalancer (K3s ServiceLB assigns an IP automatically)
kubectl expose deployment nginx --port=80 --type=LoadBalancer
# Verify — EXTERNAL-IP should appear within a few seconds
kubectl get svc nginx
Terminal window
# Delete cluster and all resources
ksail cluster delete

The ksail.yaml file controls your cluster:

apiVersion: ksail.io/v1alpha1
kind: Cluster
spec:
cluster:
name: my-cluster
distribution: K3s
provider: Docker
controlPlanes: 1
workers: 2

Customize K3d behavior in k3d.yaml:

apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: my-cluster
servers: 1 # control-plane nodes
agents: 2 # worker nodes
ports:
- port: 8080:80@loadbalancer # Map host port 8080 to cluster port 80
- port: 8443:443@loadbalancer # Map host port 8443 to cluster port 443
options:
k3s:
extraArgs:
- arg: --disable=traefik # Disable built-in Traefik ingress
nodeFilters:
- server:*

K3s includes these components by default—KSail does not reinstall them:

ComponentNotes
ServiceLB (Klipper-LB)LoadBalancer support via host ports
local-path-provisionerDynamic PersistentVolume provisioning
Metrics Serverkubectl top and HPA support
CoreDNSCluster DNS
TraefikIngress controller (can be disabled)

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:

Terminal window
ksail cluster init --name my-cluster --distribution K3s \
--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'

K3d injects registry configuration so all nodes use your mirrors automatically.

K3d uses Docker containers as cluster nodes. A dedicated K3d load balancer container routes traffic to control-plane nodes. Each control-plane container runs the K3s server binary (embedding all control-plane components, etcd, ServiceLB, local-path-provisioner, and metrics-server). Worker nodes run the K3s agent with containerd.

See the Support Matrix for a full feature and component comparison across all distributions.

Symptom: ksail cluster create fails with Docker errors.

Terminal window
docker ps # Check Docker is running
df -h # Check available disk space
docker system prune -f # WARNING: removes unused containers, networks, images, and build cache
ksail cluster create

Symptom: kubectl get nodes shows NotReady status.

Terminal window
kubectl describe node <node-name>
kubectl get pods -n kube-system
ksail cluster delete && ksail cluster create

K3s uses ServiceLB (Klipper-LB), which binds to host ports. Ensure the port is not already in use:

Terminal window
docker ps --format "table {{.Names}}\t{{.Ports}}" | grep k3d
# Use different ports in k3d.yaml
ports:
- port: 9080:80@loadbalancer

Symptom: address already in use error during cluster creation.

Terminal window
k3d cluster list
k3d cluster delete <cluster-name>
# Or initialize with a different name:
ksail cluster init --name my-cluster-2 --distribution K3s

Reduce node count in ksail.yaml (workers: 0 for a single-node cluster) and run docker system prune. Increase Docker Desktop resources under Settings → Resources.

Unlike Vanilla (Kind), K3s supports in-place worker node scaling:

# ksail.yaml — increase workers
spec:
cluster:
workers: 4
Terminal window
ksail cluster update # Adds/removes worker containers without recreation

Remove K3s default components you don’t need:

k3d.yaml
options:
k3s:
extraArgs:
- arg: --disable=traefik # Remove Traefik ingress
nodeFilters: ["server:*"]
- arg: --disable=servicelb # Remove ServiceLB (use MetalLB instead)
nodeFilters: ["server:*"]

Replace the default Flannel CNI with Cilium:

ksail.yaml
spec:
cluster:
cni: Cilium
# k3d.yaml — disable built-in Flannel
options:
k3s:
extraArgs:
- arg: --flannel-backend=none
nodeFilters: ["server:*"]
- arg: --disable-network-policy
nodeFilters: ["server:*"]

K3s version is determined by the K3d image. Pin a specific version in k3d.yaml:

k3d.yaml
image: rancher/k3s:v1.31.0-k3s1

See K3d image tags for available versions.

Expose services on host ports:

k3d.yaml
ports:
- port: 8080:80@loadbalancer # HTTP
- port: 8443:443@loadbalancer # HTTPS
- port: 30080:30080@agent:0 # NodePort on first worker

KSail generates standard k3d.yaml files—use K3d CLI directly (k3d cluster create --config k3d.yaml) if needed. No vendor lock-in: configurations are portable and KSail can interact with any K3d cluster accessible via your kubeconfig.