Raspberry Pi Cluster Setup (Kubernetes)
Build a Raspberry Pi cluster running Kubernetes. Step-by-step guide covering networking, K3s install, and multi-node workloads.
Introduction
Build a 4-node Pi cluster running Kubernetes and handle multi-machine workloads. This guide covers hardware selection, networking setup, K3s installation across nodes, shared NFS storage, and monitoring with Lens. For homelabs running multiple services, clusters offer redundancy and horizontal scaling beyond single-machine capabilities.
Prerequisites
- 3-4 Raspberry Pi 4 (8GB) or Pi 5 (8GB minimum)
- One 8-port Gigabit PoE switch (optional but recommended)
- Four 32GB+ USB SSDs for each Pi
- USB 3.0 hub + cables (if not using PoE switch)
- Ethernet cables (one per Pi)
- Static IP range reserved on router (e.g.,
192.168.1.100-110)
Hardware & Cost Breakdown
| Item | Cost | Notes |
|---|---|---|
| Pi 4 (8GB) × 4 | $360 | $90 each, or use mix of Pi 4/5 |
| USB 3.0 SSD 1TB × 4 | $200 | $50 each, critical for performance |
| PoE Switch (8 port) | $80 | Powers + networks in one cable |
| PoE HAT × 4 | $40 | For PoE power |
| Ethernet cables × 4 | $15 | CAT6 recommended |
| Total | $695 | Scales to 6-8 nodes for ~$1K |
Network Architecture
Internet
|
Router (192.168.1.1)
|
PoE Switch (192.168.1.50)
|
+-- Pi-Master (192.168.1.100) [K3s server]
+-- Pi-Worker-1 (192.168.1.101) [K3s agent]
+-- Pi-Worker-2 (192.168.1.102) [K3s agent]
+-- Pi-Worker-3 (192.168.1.103) [K3s agent]
Step 1 — Configure Static IPs
On each Pi, set static IP via systemd-networkd:
sudo nano /etc/systemd/network/99-static.network
Add:
[Match]
Name=eth0
[Network]
Address=192.168.1.100/24
Gateway=192.168.1.1
DNS=8.8.8.8 8.8.4.4
Adjust address for each Pi (100, 101, 102, 103).
Restart networking:
sudo systemctl restart systemd-networkd
ip a
Verify static IP assigned.
Step 2 — Prepare All Nodes
Run on all four Pis (master and workers):
sudo apt update && sudo apt upgrade -y
Enable required kernel modules:
sudo nano /boot/firmware/cmdline.txt
Append to end of line:
cgroup_memory=1 cgroup_enable=memory
Reboot each Pi:
sudo reboot
Step 3 — Install K3s on Master
SSH into master Pi (192.168.1.100):
curl -sfL https://get.k3s.io | sh -
Verify:
sudo k3s kubectl get nodes
Get the node token (needed for workers):
sudo cat /var/lib/rancher/k3s/server/node-token
Copy this token—you'll use it on all workers.
Step 4 — Install K3s on Worker Nodes
SSH into first worker (192.168.1.101):
curl -sfL https://get.k3s.io | \
K3S_URL=https://192.168.1.100:6443 \
K3S_TOKEN=<paste-token-here> \
sh -
Repeat for workers 102 and 103, changing token and URL only.
Step 5 — Verify Cluster
From master, check all nodes:
sudo k3s kubectl get nodes
Expected output:
NAME STATUS ROLES AGE
pi-master Ready control-plane,master 5m
pi-worker-1 Ready <none> 2m
pi-worker-2 Ready <none> 2m
pi-worker-3 Ready <none> 2m
Check pods across cluster:
kubectl get pods --all-namespaces
Step 6 — Configure Shared NFS Storage
Set up NFS server on master to share storage across cluster:
sudo apt install nfs-kernel-server -y
Create shared directory:
sudo mkdir -p /srv/nfs/shared
sudo chmod 777 /srv/nfs/shared
Export via NFS:
sudo nano /etc/exports
Add:
/srv/nfs/shared 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
Restart NFS:
sudo exportfs -a
sudo systemctl restart nfs-kernel-server
Install NFS on Workers
On each worker:
sudo apt install nfs-common -y
Create Persistent Volume in K3s
From master, create NFS-backed storage:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-shared
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany
nfs:
server: 192.168.1.100
path: "/srv/nfs/shared"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-shared-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: ""
resources:
requests:
storage: 50Gi
volumeName: nfs-shared
EOF
Verify:
kubectl get pv,pvc
Step 7 — Deploy Test Application
Deploy Nginx across cluster:
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-cluster
spec:
replicas: 4
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx:1.26-alpine
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
---
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
type: LoadBalancer
ports:
- port: 80
targetPort: 80
EOF
Check pod distribution:
kubectl get pods -o wide
Each pod should land on different nodes (pod anti-affinity).
Step 8 — Monitor Cluster with Lens
Install Lens (Kubernetes IDE) on your local machine:
# Mac
brew install lens
# Linux
snap install kontena-lens
# Windows
choco install lens
Copy kubeconfig from master:
sudo cat /etc/rancher/k3s/k3s.yaml
In Lens, click + → paste kubeconfig → select cluster. View all nodes, pods, and resource usage in real time.
Step 9 — Enable Metrics & Monitoring
Install metrics-server for kubectl top:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
Wait 30 seconds, then check:
kubectl top nodes
kubectl top pods --all-namespaces
Step 10 — Useful Cluster Operations
Add a 5th node
On new Pi, run K3s agent with same token/URL as workers.
Remove a node
Drain then delete:
kubectl drain pi-worker-2 --ignore-daemonsets
kubectl delete node pi-worker-2
On that Pi, uninstall K3s:
/usr/local/bin/k3s-agent-uninstall.sh
Restart master
All workloads survive (they're on workers). Master restarts gracefully:
sudo systemctl restart k3s
Backup cluster state
Use velero (backup tool):
helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
helm install velero vmware-tanzu/velero --namespace velero --create-namespace
Resource Usage (4-Node Cluster)
| Metric | Per Node | Total |
|---|---|---|
| RAM overhead (K3s) | ~500 MB | 2 GB |
| Available for apps | ~7.5 GB | 30 GB |
| Storage (SSD 1TB each) | 1 TB | 4 TB |
Each Pi can run ~10-15 small containers safely.
Troubleshooting
Worker stuck in NotReady
Check node logs: kubectl describe node <name>. Common: cgroup memory not enabled. Verify /boot/firmware/cmdline.txt changes and reboot.
Pods evicted (disk pressure)
SSDs filling up. Check: df -h on each node. Clean old images: docker image prune -a.
NFS mount fails
Verify NFS running on master: sudo systemctl status nfs-kernel-server. Check firewall: sudo ufw allow nfs.
Network latency between nodes
Expected on 100 Mbps Ethernet. Upgrade to Gigabit switch for 1 Gbps (15-20x faster).
Summary
A 4-node Pi cluster costs under $700 and runs hundreds of containers. Use it for redundant services, testing multi-node workloads, or learning Kubernetes at scale. Start with 3 nodes (master + 2 workers), add 4th for true HA. Monitor with Lens, manage storage with NFS, and scale applications with kubectl.