Skip to main content

Command Palette

Search for a command to run...

Building My Kubernetes Homelab with Raspberry Pi 4 - Installing Kubernetes with kubeadm

Updated
5 min read
Building My Kubernetes Homelab with Raspberry Pi 4 -  Installing Kubernetes with kubeadm
O

DevOps Engineer with a proven track record of streamlining software development and delivery processes. Skilled in automation, configuration management, and continuous integration and delivery (CI/CD), with expertise in cloud infrastructure and containerization technologies. Possess strong communication and collaboration skills, able to work effectively across development, operations, and business teams to achieve common goals. Dedicated to staying current with the latest technologies and tools in the DevOps field to drive continuous improvement and innovation.

If you’re preparing for the Certified Kubernetes Administrator (CKA) exam, reading documentation alone isn’t enough. You need a real environment where you can install Kubernetes, break it, debug it, and rebuild it from scratch.

That’s exactly why I built a Kubernetes homelab using Raspberry Pi 4s.

In Part 1, I covered the hardware setup.
In Part 2, I installed Ubuntu Server 22.04 LTS and configured networking.

In this post, I’ll walk through installing a production‑grade Kubernetes cluster using kubeadm on ARM64 Raspberry Pi hardware. This is a real kubeadm installation, the same style used in production, and required knowledge for the Certified Kubernetes Administrator (CKA) exam.

📦 GitHub Repository

All commands, configs, and updates for this homelab live here:
👉 https://github.com/charliepoker/k8s-raspberrypi-homelab

🧭 Cluster Overview

Node NameRoleIP Address
k8s-masterControl Plane10.0.0.212
k8s-worker-1Worker Node10.0.0.248
k8s-worker-2Worker Node10.0.0.116
  • OS: Ubuntu Server 22.04 LTS (ARM64)

  • Kubernetes: v1.29

  • Container Runtime: containerd

  • CNI: Flannel

Why kubeadm on Raspberry Pi?

For learning Kubernetes properly, kubeadm forces you to understand:

  • How the control plane boots

  • kubelet + container runtime interaction

  • CNI networking fundamentals

  • Common failure modes (and how to recover)

Tools like k3s are great, but they abstract too much. For CKA preparation, kubeadm is the right tool.

Before We Start: What Are We Installing?

This guide installs four core components:

ComponentWhat it does
containerdRuns containers on each node
kubeletNode agent (runs on every node)
kubeadmBootstrap the Kubernetes cluster
kubectlCommand‑line tool to manage Kubernetes

📌 Important:
Everything up to kubeadm init must be done on ALL nodes (master + workers).

Phase 1: System Preparation (All Nodes)

These steps are mandatory. Skipping any of them will cause kubeadm to fail, especially on Raspberry Pi.

1️⃣ Update the System

sudo apt update && sudo apt upgrade -y

2️⃣ Disable Swap (Kubernetes Requirement)

Kubernetes will not start if swap is enabled.

sudo swapoff -a
sudo sed -i '/ swap / s/^.*$/#&/g' /etc/fstab
free -h

Swap disabled and verified on all Raspberry Pi nodes

3️⃣ Enable Required Kernel Modules

sudo modprobe overlay
sudo modprobe br_netfilter

Persist across reboots:

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

4️⃣ Configure Kernel Networking Parameters

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

sudo sysctl --system

5️⃣ Enable cgroups (Critical on Raspberry Pi)

This is non‑optional on ARM hardware.

Edit the kernel command line:

sudo nano /boot/firmware/cmdline.txt

Append to the same line:

cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1

Reboot:

sudo reboot

Verify: ls /sys/fs/cgroup | grep memory

Phase 2: Install containerd (All Nodes)

Kubernetes needs a container runtime. I chose containerd for its simplicity and kubeadm compatibility.

6️⃣ Install containerd

sudo apt update
sudo apt install -y containerd

7️⃣ Configure containerd for Kubernetes

Generate default config:

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null

Edit the file:

sudo nano /etc/containerd/config.toml

Enable systemd cgroups

SystemdCgroup = true

Correct pause image (ARM-safe)

sandbox_image = "registry.k8s.io/pause:3.9"

Restart containerd:

sudo systemctl restart containerd
sudo systemctl enable containerd
systemctl status containerd

Phase 3: Install Kubernetes Packages. This is where Kubernetes itself is installed.

Run everything below on ALL nodes.

8️⃣ Add the Kubernetes APT Repository

sudo mkdir -p /etc/apt/keyrings

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | \
sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] \
https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /" | \
sudo tee /etc/apt/sources.list.d/kubernetes.list

9️⃣ Install kubelet, kubeadm, and kubectl

📌 This is the exact moment Kubernetes gets installed on the node.

sudo apt update
sudo apt install -y kubelet kubeadm kubectl

Prevent automatic upgrades:

sudo apt-mark hold kubelet kubeadm kubectl

Enable kubelet:

sudo systemctl enable kubelet

Phase 4: Initialize the Control Plane (MASTER NODE ONLY)

Now, Kubernetes actually becomes a cluster.

🔟 Initialize the Cluster

Run only on k8s-master:

sudo kubeadm init \
  --apiserver-advertise-address=10.0.0.212 \
  --pod-network-cidr=10.244.0.0/16 \
  --node-name=k8s-master \
  --cri-socket=unix:///run/containerd/containerd.sock

Save the kubeadm join command shown at the end.

1️⃣1️⃣ Configure kubectl Access (Master Node)

mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Test:

kubectl get nodes

Phase 5: Install Pod Networking (CNI)

Without networking, nodes will stay NotReady.

1️⃣2️⃣ Install Flannel

kubectl apply -f \
https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

Wait for readiness:

kubectl wait --for=condition=ready pod \
-l app=flannel -n kube-flannel --timeout=300s

Phase 6: Join Worker Nodes

Run the saved join command on each worker node:

sudo kubeadm join 10.0.0.212:6443 --token <token> \
  --discovery-token-ca-cert-hash sha256:<hash> \
  --cri-socket=unix:///run/containerd/containerd.sock

Phase 7: Verify the Cluster (Master Node)

kubectl get nodes

kubectl get pods -A

Phase 8: (Optional) Re‑Enable UFW Safely

Once the cluster is stable, we can re‑enable UFW with the correct rules.

🔐 Control Plane Node (k8s‑master)

sudo ufw allow 6443/tcp
sudo ufw allow 2379:2380/tcp
sudo ufw allow 10250/tcp
sudo ufw allow 10259/tcp
sudo ufw allow 10257/tcp

Flannel & Cluster Traffic

sudo ufw allow 8472/udp
sudo ufw allow from 10.0.0.0/24
sudo ufw allow from 10.244.0.0/16

🔐 Worker Nodes

sudo ufw allow 10250/tcp
sudo ufw allow 30000:32767/tcp


Phase 9: Label Worker Nodes

Run on the master node:

kubectl label node k8s-worker-1 node-role.kubernetes.io/worker=worker
kubectl label node k8s-worker-2 node-role.kubernetes.io/worker=worker

Verify:

kubectl get nodes

Phase 10: Final Verification

bashkubectl cluster-info

Final Result

✅ Kubernetes installed
✅ Control plane running
✅ Worker nodes joined
✅ Raspberry Pi homelab ready

Lessons Learned

  • Raspberry Pi requires cgroups

  • SystemdCgroup = true is mandatory

  • Pause image mismatches break kubeadm

  • When kubeadm fails, reset fully:

sudo kubeadm reset -f
sudo rm -rf /etc/kubernetes /var/lib/etcd /var/lib/kubelet ~/.kube