Build a K3s Cluster on Raspberry Pi

Published on

Series: K3s on Raspberry Pi

  1. Build a K3s Cluster on Raspberry Pi ← this article
  2. Deploy a Go Service to K3s
  3. GitOps on K3s with ArgoCD
  4. Multi-Environment K3s with Kustomize

A minimal guide to building a Raspberry Pi K3s cluster. Covers one control-plane node and one or more worker nodes.

Hardware used: Raspberry Pi 4 (4 GB) × 3, SD cards (32 GB+), a network switch, and ethernet cables.

1. Flash SD Cards

OS Lite has no desktop environment, keeping RAM usage low - critical for a node that needs to run workloads. Raspberry Pi Imager lets you pre-configure hostname, SSH, and credentials before first boot so each Pi is ready without a monitor.

Use Raspberry Pi Imager to flash Raspberry Pi OS Lite (64-bit) onto each SD card.

Select the board, OS, and storage drive:

In OS Customisation, configure each node before flashing:

SettingValue
Hostnamepi01, pi02, pi03
Username / Passwordyour choice
Locale / Timezone
SSHEnable (password auth)

Repeat for every SD card with a different hostname.

2. First Boot & SSH

SSH lets you manage all nodes without a monitor from your laptop. Finding the DHCP-assigned IP first is necessary before setting a static one.

ssh [email protected]

Elevate sudo credentials once so subsequent sudo commands don’t prompt:

sudo -v

Update the OS:

sudo apt-get update -y && sudo apt-get upgrade -y && sudo apt-get dist-upgrade -y
sudo apt --fix-broken install -y

3. Set a Static IP

Kubernetes nodes must have stable IPs - if a node’s IP changes after reboot, the cluster loses track of it. Static IPs also make SSH and kubectl configuration predictable.

Find your gateway and connection name:

ip route
# default via 192.168.0.1 dev eth0 proto dhcp src 192.168.0.134 metric 100

nmcli connection show
# NAME                UUID   TYPE      DEVICE
# Wired connection 1  ...    ethernet  eth0

Assign a static IP (adjust address per node: .51, .52, .53):

sudo nmcli connection modify "Wired connection 1" \
  ipv4.addresses 192.168.0.51/24 \
  ipv4.gateway   192.168.0.1 \
  ipv4.dns       "192.168.0.1 1.1.1.1 8.8.8.8" \
  ipv4.method    manual

sudo reboot

Router IP range reference:

4. Prepare Each Node

Repeat the following steps on every node.

Enable cgroup memory

K3s uses cgroups to enforce CPU and memory limits on containers. Without this flag, the kernel does not expose memory accounting and K3s will fail to start or will ignore resource limits. Add to the end of the single line in /boot/firmware/cmdline.txt:

sudo nano /boot/firmware/cmdline.txt
# append below line without '#' (do not add a new line):
# cgroup_memory=1 cgroup_enable=memory

Verify after reboot:

sudo reboot
grep cgroup /proc/filesystems
# nodev   cgroup
# nodev   cgroup2

Disable swap

Kubernetes assumes it has full control over memory. Swap lets the OS silently move memory to disk, which breaks the scheduler’s resource accounting and causes unpredictable pod behavior.

sudo systemctl disable --now dphys-swapfile
sudo apt remove -y dphys-swapfile
sudo swapoff -a
sudo reboot

Confirm swap is gone:

free -h
# Swap:          0B       0B       0B

Install iptables

K3s uses iptables to manage pod-to-pod networking and service routing. The iptables-persistent package ensures rules survive a reboot.

sudo apt install -y iptables iptables-persistent
iptables --version

5. Install K3s - Control Plane

K3s is a lightweight Kubernetes distribution built for ARM and edge hardware. The control plane runs the API server, scheduler, and controller - all other nodes register against it.

Run on pi01 only:

curl -sfL https://get.k3s.io | sh -

Verify:

sudo systemctl status k3s
sudo k3s kubectl get nodes
sudo k3s kubectl get pods -A

Get the node token (needed for worker nodes):

sudo cat /var/lib/rancher/k3s/server/node-token

6. Join Worker Nodes

Worker nodes run your actual workloads. The token proves to the control plane that the joining node is authorized - without it, any machine could join your cluster.

Run on each worker node (pi02, pi03, …):

export K3S_URL="https://192.168.0.51:6443"    # control-plane static IP
export K3S_TOKEN="<paste-token-from-above>"

curl -sfL https://get.k3s.io | K3S_URL=$K3S_URL K3S_TOKEN=$K3S_TOKEN sh -

Verify the agent is running:

sudo systemctl status k3s-agent
sudo journalctl -u k3s-agent -f

Check all nodes from the control plane:

sudo k3s kubectl get nodes -o wide
# NAME   STATUS   ROLES                  AGE   VERSION
# pi01   Ready    control-plane,master   5m    v1.32.x
# pi02   Ready    <none>                 2m    v1.32.x
# pi03   Ready    <none>                 2m    v1.32.x

7. Configure kubectl

Add a permanent alias and kubeconfig in ~/.bashrc:

export KUBECONFIG=~/.kube/pi-cluster.yaml
alias k=kubectl

Useful Commands

Quick reference for day-to-day cluster management.

kubectl get nodes -o wide               # cluster overview
kubectl get pods -A                     # all pods across namespaces
kubectl top pod                         # resource usage (needs metrics-server)
kubectl config set-context --current --namespace=my-ns   # switch namespace
kubectl apply -f manifest.yaml
kubectl delete -f manifest.yaml

Uninstall K3s

K3s ships its own uninstall scripts that cleanly remove all binaries, services, and network interfaces it created.

# control plane
/usr/local/bin/k3s-uninstall.sh

# worker nodes
/usr/local/bin/k3s-agent-uninstall.sh