// 3-NODE K3S CLUSTER ON RASPBERRY PI 5
HOMELAB
HOMELAB
A small Kubernetes cluster I'm building at home, on three Raspberry Pi 5 boards. The goal is to replicate at smaller scale the kind of infrastructure I run at work — k3s, distributed storage, GitOps, identity, observability — so I can break things in a place where it doesn't matter and learn the layers properly.
Everything is in a public repo: github.com/MagyDrop17/homelab. Ansible playbooks, Flux manifests, phase notes — work in progress and will stay that way for a while.
// HARDWARE
NODES
3 × Raspberry Pi 5 — 16 GB RAM
STORAGE
1 TB NVMe per node (data) + 117 GB SD card (OS)
NETWORK
Flat home LAN, gigabit
// NETWORK
rp-node-1
192.168.1.230
control plane + worker
rp-node-2
192.168.1.231
control plane + worker
rp-node-3
192.168.1.232
control plane + worker
cluster VIP
192.168.1.240
floating API endpoint (kube-vip)
// STACK
OSUbuntu Server 24.04 LTS (ARM64)
Kubernetesk3s, HA with embedded etcd (3 control planes)
VIPkube-vip (ARP mode)
StorageLonghorn — replicated block storage on NVMes
Load balancerMetalLB
Ingressingress-nginx
TLScert-manager
GitOpsFluxCD
Git serverForgejo (will eventually host this repo)
IdentityKeycloak + PrivacyIDEA
VPNNetBird
ObservabilityPrometheus + Grafana + Loki
BackupsVelero
Some of these are running already, some are still on the roadmap — see phases below.
// PHASES
PHASE 0OKWorkstation prep (WSL2 + toolchain)
PHASE 1OKOS hardening with Ansible
PHASE 2OKk3s install in HA mode
PHASE 3OKPlatform layer — MetalLB, ingress-nginx, cert-manager, Longhorn
PHASE 4OKFlux bootstrap and app-of-apps pattern
PHASE 5OKForgejo, then migrate Flux source from GitHub to self-hosted
PHASE 6WIPIdentity (Keycloak + PrivacyIDEA), NetBird VPN, app workloads
PHASE 7···Observability (Prometheus / Grafana / Loki) and backups (Velero)
// NOTES
- ›The cluster is private — no public ingress yet, no port forwarding, no tunnel
- ›This website is not hosted on the cluster — it lives on a separate VPS (see colophon)
- ›Whether the website ever moves onto the cluster is undecided