Skip to content
Cloud & Infra Intermediate Tutorial

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.

Lenn Voss
Lenn Voss
Cloud & Infrastructure Writer · Aug 14, 2026 · 5 min read
Bootstrap a Multi-Node k3s Kubernetes Cluster on Raspberry Pis

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), .51 and .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

  1. K3s Quick-Start Guide — docs.k3s.io
  2. K3s Requirements — docs.k3s.io
  3. K3s Configuration Options — docs.k3s.io
  4. K3s Cluster Access — docs.k3s.io
  5. k3s Releases (v1.36.3+k3s1) — github.com
  6. Trixie - the new version of Raspberry Pi OS — raspberrypi.com
Lenn Voss
Written by
Lenn Voss · Cloud & Infrastructure Writer

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

Join the discussion

Sign in or create an account to comment and vote.

No comments yet

Be the first to weigh in.

Related Reading