Bootstrap a Multi-Node k3s Kubernetes Cluster on Raspberry Pis
Turn a stack of Raspberry Pis into a real Kubernetes cluster with k3s, remote kubectl, and a deployed workload.
What you'll build
A real three-node Kubernetes cluster running k3s on Raspberry Pis — one control-plane node, two workers — with kubectl access from your laptop and an nginx deployment spread across the nodes. Everything here works the same with two Pis or ten.
Prerequisites
Verified against k3s v1.36.3+k3s1 (Kubernetes v1.36.3, current stable) on Raspberry Pi OS Lite 64-bit (Trixie), August 2026.
- 3× Raspberry Pi 4 or 5 with at least 2 GB RAM (the control plane wants 2 GB; workers get by on less). Pi 3s work but are slow.
- An SD card (or better, a USB SSD — k3s is backed by SQLite and SD cards are the top cause of flaky clusters) per Pi.
- All Pis wired to the same LAN, each with a static IP or DHCP reservation. This tutorial uses
192.168.1.50(server),.51and.52(workers) — substitute yours. - kubectl on your workstation.
- k3s needs TCP 6443 open to the server and UDP 8472 between all nodes (Flannel VXLAN). A flat home LAN with no firewall between the Pis needs no action.
1. Flash and prep the Pis
Flash Raspberry Pi OS Lite (64-bit) onto each card with Raspberry Pi Imager. In the Imager's OS customisation dialog, for each Pi:
- Set a unique hostname:
pi-cp,pi-w1,pi-w2. k3s requires unique hostnames per node — duplicate names make joins misbehave. - Set a username and password, and enable SSH.
Boot all three and confirm you can reach them:
ssh pi@192.168.1.50 hostname # → pi-cp
2. Enable memory cgroups on every Pi
Raspberry Pi OS ships with the memory cgroup disabled, and k3s hard-fails without it. On each Pi, append the flags to the kernel command line — it's a single line in /boot/firmware/cmdline.txt, and the parameters must stay on that one line:
sudo sed -i '$ s/$/ cgroup_memory=1 cgroup_enable=memory/' /boot/firmware/cmdline.txt
sudo reboot
After reboot, confirm the memory controller is live:
cat /sys/fs/cgroup/cgroup.controllers
# cpuset cpu io memory hugetlb pids rdma misc
If memory is in that list, you're good.
3. Install the k3s server
On pi-cp (192.168.1.50):
curl -sfL https://get.k3s.io | sh -s - --write-kubeconfig-mode 644
The script installs the latest stable k3s, registers a systemd service, and starts it. --write-kubeconfig-mode 644 makes the kubeconfig world-readable so you can copy it off the Pi without sudo — fine on a home LAN, since the file grants cluster-admin.
Grab the join token; workers authenticate with it:
sudo cat /var/lib/rancher/k3s/server/node-token
# K10c8f2…::server:8a41b2…
4. Join the workers
On pi-w1 and pi-w2, run the same installer with K3S_URL set — that alone puts it in agent mode:
curl -sfL https://get.k3s.io | \
K3S_URL=https://192.168.1.50:6443 \
K3S_TOKEN=<paste-node-token-here> sh -
Each worker gets a k3s-agent systemd service and registers with the control plane within a few seconds.
5. Point kubectl at the cluster
The server writes an admin kubeconfig to /etc/rancher/k3s/k3s.yaml, with the API address set to 127.0.0.1. Copy it to your workstation and swap in the server's LAN IP:
scp pi@192.168.1.50:/etc/rancher/k3s/k3s.yaml ~/.kube/k3s-pi.yaml
sed -i 's/127.0.0.1/192.168.1.50/' ~/.kube/k3s-pi.yaml # macOS: sed -i '' 's/…/…/'
export KUBECONFIG=~/.kube/k3s-pi.yaml
6. Deploy your first workload
Three nginx replicas behind a NodePort service:
kubectl create deployment web --image=nginx --replicas=3
kubectl expose deployment web --port=80 --type=NodePort
Verify it works
All three nodes should be Ready:
kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION
pi-cp Ready control-plane,master 8m v1.36.3+k3s1
pi-w1 Ready <none> 3m v1.36.3+k3s1
pi-w2 Ready <none> 2m v1.36.3+k3s1
Find the assigned NodePort and hit it — any node's IP works, k3s routes it to a pod:
kubectl get svc web
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# web NodePort 10.43.12.87 <none> 80:31584/TCP 30s
curl -s http://192.168.1.51:31584 | grep title
# <title>Welcome to nginx!</title>
kubectl get pods -o wide should show the replicas spread across nodes. That's a working multi-node cluster.
Troubleshooting
Failed to find memory cgroup, you may need to add "cgroup_memory=1 cgroup_enable=memory" to your linux cmdline (/boot/cmdline.txt on a Raspberry Pi) — k3s crash-loops with this in journalctl -u k3s when step 2 was skipped, or the flags landed on a new line. cmdline.txt must be exactly one line; re-check it, reboot, then sudo systemctl restart k3s. (Despite the message, the file is /boot/firmware/cmdline.txt on current Raspberry Pi OS.)
WARN[0000] Unable to read /etc/rancher/k3s/k3s.yaml, please start server with --write-kubeconfig-mode or --write-kubeconfig-group to modify kube config permissions — you ran kubectl on the Pi but installed the server without the flag from step 3. Re-run the installer with --write-kubeconfig-mode 644 (it's idempotent and preserves the cluster), or just prefix your commands with sudo.
failed to get CA certs: Get "https://192.168.1.50:6443/cacerts": dial tcp 192.168.1.50:6443: connect: connection refused — in journalctl -u k3s-agent on a worker: the agent can't reach the API server. Confirm k3s is actually running on the server (sudo systemctl status k3s), the IP in K3S_URL is right, and nothing is filtering TCP 6443.
Nodes are Ready but pods can't talk across nodes — if you chose Ubuntu instead of Raspberry Pi OS: Ubuntu 21.10–23.10 on Pi ships without the VXLAN kernel module Flannel needs. Fix with sudo apt install linux-modules-extra-raspi and reboot. Ubuntu 24.04+ includes it.
Next steps
- Add high availability: k3s supports embedded etcd — three server nodes instead of one, started with
--cluster-init. - Expose services properly with the bundled Traefik ingress controller instead of NodePorts.
- Wire up GitOps: point Flux or Argo CD at the cluster and stop
kubectl apply-ing by hand. - When you outgrow the SD cards, move the datastore to an external database or SSD-backed storage — it's the single biggest reliability win on Pi hardware.
Sources & further reading
- K3s Quick-Start Guide — docs.k3s.io
- K3s Requirements — docs.k3s.io
- K3s Configuration Options — docs.k3s.io
- K3s Cluster Access — docs.k3s.io
- k3s Releases (v1.36.3+k3s1) — github.com
- Trixie - the new version of Raspberry Pi OS — raspberrypi.com
Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.
Discussion 0
No comments yet
Be the first to weigh in.