Build your lab cluster

Install Docker, kind, kubectl and Helm, bring up the three-node lab every task in this course runs against, and learn how to check, reset and tear it down.

Foundations45 min2 tasks

After this lesson

  • 01You can bring up the cka kind cluster with one command and explain what each add-on is for.
  • 02You can open a shell on a lab node and say how that differs from ssh on the exam.
  • 03You can diagnose the three common lab failures (Docker down, port in use, slow pulls) without re-reading this page.

Every task in this course ends with a verify script, and that script needs a cluster to look at. The lab is a kind cluster: three Kubernetes nodes running as Docker containers on your machine. It is free, it builds in a few minutes, it resets in a few minutes, and it is close enough to the exam clusters for everything except the kubeadm lifecycle work (installing, upgrading, joining nodes), which needs real VMs and gets its own optional section at the end.

Install the tools

macOS: Docker Desktop or Colima, then kind, kubectl and helmbash
# Option A: Docker Desktop (give it at least 4 GB memory and 2 CPUs under Settings > Resources)
brew install --cask docker
open -a Docker

# Option B: Colima, a lighter Docker runtime for macOS
brew install colima docker
colima start --cpu 4 --memory 6

# The cluster tools
brew install kind kubectl helm

docker info >/dev/null && echo "docker ok"
kind version && kubectl version --client && helm version --short
Ubuntu 24.04 (and Windows inside a WSL2 Ubuntu shell)bash
# Docker Engine from Docker's apt repository
sudo apt-get update && sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker "$USER" && newgrp docker

# kind: a single release binary (pick amd64 or arm64)
arch=amd64; [ "$(uname -m)" = aarch64 ] && arch=arm64
curl -Lo kind "https://kind.sigs.k8s.io/dl/v0.32.0/kind-linux-$arch"
chmod +x kind && sudo mv kind /usr/local/bin/kind

# kubectl: the current stable release
curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/$arch/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

# helm: the release tarball
curl -fsSL "https://get.helm.sh/helm-v4.2.3-linux-$arch.tar.gz" | tar -xz
sudo mv "linux-$arch/helm" /usr/local/bin/helm && rm -rf "linux-$arch"

# kind needs more inotify headroom than Ubuntu ships with
sudo sysctl fs.inotify.max_user_watches=524288 fs.inotify.max_user_instances=512

Windows: install Docker Desktop with the WSL 2 backend, install Ubuntu from the Microsoft Store, enable it under Docker Desktop > Settings > Resources > WSL integration, and then do everything in this course from the Ubuntu shell using the block above (skip the Docker Engine part; Docker Desktop provides the docker command inside WSL). kubectl version: kubectl supports one minor version of skew either way from the API server. After the cluster is up, compare kubectl version client and server; if the client is more than one minor away, install the matching release from https://dl.k8s.io/release/v1.NN.0/bin/<os>/<arch>/kubectl. The exam runs v1.37, so a v1.37 kubectl on a v1.36 or v1.37 lab is ideal. Resources: three nodes plus the add-ons need about 4 GB of memory for Docker and 2 CPUs; 6 GB is comfortable.

Bring the lab up

One command, three to six minutes on first runbash
curl -fsSL https://codewithmuh.com/labs/cka/lab-up.sh | bash

# Prefer to read it first? Same thing, two steps:
curl -fsSLO https://codewithmuh.com/labs/cka/lab-up.sh && less lab-up.sh && bash lab-up.sh

# When it finishes you should see three Ready nodes and the summary block:
kubectl get nodes -o wide
kubectl config current-context      # kind-cka
  • A kind cluster named cka from kind-config.yaml: one control plane (cka-control-plane) and two workers (cka-worker, cka-worker2), kubectl context kind-cka. kind's default CNI is disabled so Calico can take its place.
  • Calico as the pod network. The exam's NetworkPolicy tasks only make sense on a CNI that enforces policies; kind's built-in kindnet does not.
  • metrics-server (with --kubelet-insecure-tls, because kind's kubelet certificates are self-signed) so kubectl top nodes and kubectl top pods return numbers. It needs a minute of samples after install.
  • ingress-nginx bound to the control-plane node, with the node's ports 80 and 443 mapped to localhost:8080 and localhost:8443. An Ingress you create is reachable with curl -H 'Host: shop.example.com' http://localhost:8080/. The IngressClass is nginx.
  • Gateway API standard CRDs so Gateway and HTTPRoute objects validate and you can practice writing them. There is no Gateway controller, so routes do not serve traffic; the exam checks the objects.
  • NodePort 30080 on the control plane is mapped to localhost:30080. A NodePort Service that asks for port 30080 is reachable from your laptop; other NodePorts are reachable from inside the cluster or from a node shell.
  • /tmp/cka, the directory every task writes answer files into. The exam uses paths like /opt/course/3/answer.txt; same idea, different prefix.

Getting a shell on a node. kind nodes are containers, so the node shell is docker exec -it cka-control-plane bash (or cka-worker, cka-worker2). You land as root with systemctl, journalctl, crictl and /etc/kubernetes where you would expect them. On the exam the equivalent is ssh <node> from the task host followed by sudo -i; every node-level task in this course shows both. One real difference: kind nodes run kubelet under systemd inside a container, so a kubelet restart is fast and a node reboot is not a thing.

Check, reset, tear downbash
# Health check: one PASS line per component, RESULT: PASS at the end. Run it when anything looks off.
curl -fsSL https://codewithmuh.com/labs/cka/lab-check.sh | bash

# Reset: deletes the cluster and runs lab-up again (two to four minutes). Use it between mock exams
# so leftover namespaces, taints and cordoned nodes from one attempt do not pollute the next.
curl -fsSL https://codewithmuh.com/labs/cka/lab-reset.sh | bash

# Tear down: deletes the cluster. Nothing else on your machine is touched; lab-up rebuilds it.
curl -fsSL https://codewithmuh.com/labs/cka/lab-down.sh | bash

# lab-up is idempotent: re-running it on an existing cluster just re-applies the add-ons.
SymptomCauseFix
"Docker is not running" from lab-up, or docker info hangsThe Docker daemon is downStart Docker Desktop; colima start; or sudo systemctl start docker on Linux
kind create cluster fails with "port is already allocated" for 8080, 8443 or 30080Another process owns the host portFind it with lsof -i :8080 (or ss -ltnp) and stop it, or download kind-config.yaml, change the hostPort values, and run lab-up.sh from that directory; it uses a local kind-config.yaml when one is present
calico-node or ingress-nginx pods sit in ContainerCreating for minutesSlow image pulls on first runWait; watch with kubectl -n kube-system get pods -w. Re-run lab-up afterwards, it picks up where it left off
Nodes NotReady for more than two minutesCNI not yet up, or inotify limits on Linuxkubectl -n kube-system get pods; raise fs.inotify limits (see the Ubuntu block) and re-run lab-up
kubectl top says metrics not availablemetrics-server has not collected a sample yetWait 60 seconds and retry
Cluster exists but context is missing after a rebootkubeconfig entry removed or Docker restarted the containerskind export kubeconfig --name cka; then lab-check
Everything is strange and you are not sure whyDrift from earlier taskslab-reset.sh

Optional: a kubeadm VM lab for the lifecycle lessons. If you want to run an upgrade or join a node with your own hands, create two or three Ubuntu 24.04 VMs with 2 CPUs and 2 to 4 GB each. With Multipass: multipass launch 24.04 --name cp --cpus 2 --memory 4G --disk 20G and the same for w1 and w2, then multipass shell cp. With Vagrant, a bento/ubuntu-24.04 box per node on a private network does the same job. The procedure (container runtime, kubeadm packages from pkgs.k8s.io, kubeadm init, a CNI, kubeadm join, then the minor-version upgrade) is in Installing a cluster with kubeadm and Upgrading clusters and draining nodes. Do it once, slowly, the week before a mock; the kind lab covers everything else.

In the docs · the tab you get on exam day

Taskfd-lab-1
Architecture2%~3 min

context · kubectl config use-context kind-cka

Prove the lab is healthy

Bring the lab up and make every line of the lab check pass: context kind-cka exists, three nodes are Ready, Calico is installed, kubectl top nodes returns data, ingress-nginx and the Gateway API CRDs are installed, docker exec cka-control-plane true works, and /tmp/cka exists.
  1. 01

    Nothing to set up

    Start on a healthy lab. Check it with lab-check.sh if unsure.

  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-lab-1/verify | bash
    Counts toward your readiness score.
Taskfd-lab-2
Workloads2%~3 min

context · kubectl config use-context kind-cka

First Pod on the lab

Create a namespace named fd-hello. In it, create a Pod named hello running the image nginx:1.27. The Pod must be in the Running state when you are done.
  1. 01

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-lab-2/setup | bash
  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-lab-2/verify | bash
    Counts toward your readiness score.
  4. 04

    Clean up

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-lab-2/cleanup | bash

Finished reading?

0 of 2 tasks passed. Readiness counts tasks, so run them before you move on.