k3d for Local Dev & Ephemeral CI Clusters
Spin up throwaway k3s clusters in Docker for CI, import local images, and mirror registries to keep pipelines fast.
k3d runs k3s nodes as Docker containers, which makes it the natural fit for CI: create a real cluster in seconds, run your tests against actual Kubernetes, then delete it — no leftover state.
CI-friendly tricks
- Import local images with
k3d image importso a freshly built image is available without pushing to a registry — great for testing the exact artifact you just built. - Wait for readiness:
k3d cluster create --waitblocks until the API and add-ons are up, so your pipeline does not race a half-started cluster. - Map the loadbalancer port so an Ingress is reachable from the CI runner on a known host port.
- Register a mirror via
--registry-configto pull through a cache and dodge Docker Hub rate limits in CI.
Fidelity
Because k3d runs the same k3s binary, behaviour matches your edge and production nodes closely — far more faithful than a mock. Reserve plain k3s-on-host for real edge/production; use k3d for laptops and pipelines.
Example
# Ephemeral CI cluster, waits until fully ready
k3d cluster create ci --agents 1 --wait \
--port "8080:80@loadbalancer" \
--registry-config /etc/rancher/k3s/registries.yaml
# Side-load the image you just built (no registry needed)
docker build -t myapp:ci .
k3d image import myapp:ci -c ci
# Run the test suite, then always clean up
kubectl apply -f k8s/ && ./run-tests.sh
k3d cluster delete ciWhen to use it
- A GitHub Actions workflow creates a k3d cluster at job start, runs Helm chart smoke tests against it, and deletes it on job exit to keep pipelines clean.
- A developer imports a locally built Docker image into k3d and deploys it with imagePullPolicy Never to test without pushing to any registry.
- A team configures k3d with a registry mirror pointing to their internal cache so CI pipeline pulls are fast and never rate-limited by Docker Hub.
More examples
Create k3d cluster for CI
Creates a lightweight k3d cluster without a load balancer or Traefik for fast CI startup, then waits for all nodes to be Ready.
k3d cluster create ci-test \
--agents 1 \
--no-lb \
--k3s-arg '--disable=traefik@server:*'
kubectl cluster-info
kubectl wait --for=condition=Ready node --all --timeout=60sImport local image and deploy
Builds a local image, imports it directly into the k3d cluster's container runtime, and verifies the pod starts correctly.
docker build -t myapp:ci-test .
k3d image import myapp:ci-test -c ci-test
kubectl run myapp --image=myapp:ci-test \
--image-pull-policy=Never --restart=Never
kubectl wait pod/myapp --for=condition=Ready --timeout=30sUse k3d with a registry mirror
Creates a k3d-managed registry and configures the cluster to use it as a mirror, enabling fast local image pulls in CI.
k3d registry create my-cache --port 5111
k3d cluster create ci-test \
--registry-use k3d-my-cache:5111
# Push to the registry and pull in CI:
docker tag myapp:latest k3d-my-cache:5111/myapp:latest
docker push k3d-my-cache:5111/myapp:latest
Discussion