guest@k3s-homelab:~$ whoami

Ein k3s-Cluster aus drei alten Notebooks

Lernprojekt, entstanden über einen langen Winter in Mecklenburg-Vorpommern — aus der Frage "wie weit komme ich mit dem, was schon in der Schublade liegt" wurde ein GitOps-verwalteter Kubernetes-Cluster mit rund zwanzig selbst gehosteten Diensten.

$neofetch --hardware Hardware & Netz

Kein Rack, kein Serverraum — drei ausgemusterte Notebooks mit kaputtem Display (macht nichts, laufen eh headless), alle auf Debian, eine USV, und zwei physisch getrennte Netze. Nach außen sichtbar ist nur, was über VPN und einen zentralen Proxy laufen darf.

topologie.svg
DSLLTE*.k3s.muellerchen.orgLoadbalancer (OPNsense)VPN only (WireGuard)OPNsenseGateway / Reverse Proxy192.0.2.1 (Beispiel)Hetzner Storage BoxOffsite-Ziel für resticCLUSTER-NETZ · 192.0.2.0/24 · über OPNsense1G-SwitchCluster & Ingressnode1control-plane ·GPUnode2control-plane ·GPUnode3control-planeSTORAGE-NETZ · 2,5G · 198.51.100.0/242,5G-SwitchNodes, NFS, QNAPNFS-Servereigener RechnerQNAPLokales Backup-ZielOffsiteRestic-Backup
Nodes
3× Notebook2× mit dedizierter GPU
Betriebssystem
Debianauf allen drei Nodes
Displays
alle drei defektläuft ohnehin headless, also egal
Stromausfall-Schutz
USV + Notebook-AkkusDetails weiter unten
Netz-Trennung
2 physisch getrennte NetzeCluster/OPNsense-Traffic komplett getrennt vom Storage-Netz
Storage-Anbindung
eigener 2,5G-Switchverbindet die 3 Nodes mit NFS-Server und QNAP (lokales Backup-Ziel) — kein Umweg über OPNsense
Offsite-Backup
Hetzner Storage Boxrestic, läuft über OPNsense/Internet — nicht über das isolierte Storage-Netz
Außenzugriff
nur via VPNzwei Ausnahmen: Birdcam & Your Spotify
Öffentliches DNS
Wildcard *.k3s.muellerchen.orgzeigt auf die interne Proxy-IP
$ kubectl get nodes -o wide (per Hand ergänzt um dmidecode/nvidia-smi)
NodeModellCPURAMGPUStorage
node1 · control-planeHP ZBook 15v G5i7-8850H @ 2.6 GHz, 12T16 GBQuadro P600 4 GBWD Black SN850X 1 TB NVMe
node2 · control-planeHP ZBook 15v G5i7-8850H @ 2.6 GHz, 12T16 GBQuadro P600 4 GBCrucial P3 1 TB NVMe
node3 · control-planeHP ProBook x360 440 G1i5-8250U @ 1.6 GHz, 8T16 GBSamsung OEM 512 GB NVMe

Alle drei auf Debian 13 (trixie), Kernel 6.12, k3s v1.36.3+k3s1 — node3 ist der Schwächling der Gruppe.

Alle drei Nodes laufen als gleichberechtigte Server-Nodes mit embedded etcd. Per keepalived handeln sie sich eine virtuelle IP aus — node2 bevorzugt (Priorität 150), node1 (120) und node3 (110) als Fallback. Im Test lief der Cluster bei einem node2-Ausfall weiter über node1 und node3.

Die USV ist eine Green Cell PowerCore 1200VA/800W — die bringt keinen eigenen Akku mit, sondern ist explizit für externe 12-V-Batterien gedacht. Hier hängt eine 100-Ah-Starterbatterie dran, das bringt bei einem Stromausfall etwa 2–3 Stunden Laufzeit für Nodes, NFS-Server und beide Switches. Die drei Notebooks haben zwar zusätzlich noch ihre eigenen, funktionierenden Akkus — die retten aber nur die Nodes selbst, nicht den NFS-Server. Ist die USV leer, ist das Storage-Netz weg, egal wie viel Akku die Notebooks noch haben.

$kubectl cluster-info Cluster-Architektur

Der eigentliche Cluster läuft schlank — k3s statt vollem Kubernetes, dazu ein kompakter Unterbau:

  • k3sLeichtgewichtige Kubernetes-Distribution — läuft schlank genug für drei alte Notebooks, ohne dass Funktionen fehlen.
  • TraefikIngress-Controller — jeder Dienst bekommt seinen eigenen Hostnamen unter *.k3s.muellerchen.org.
  • MetalLBVergibt LoadBalancer-IPs aus dem eigenen Netz — nur für die Handvoll Nicht-HTTP-Dienste nötig (DNS, SSH, …), der Rest läuft über Traefiks Ingress.
  • LonghornVerteilter Storage über die Nodes — Daten überleben, auch wenn ein Notebook mal ausfällt.
  • ArgoCDGitOps-Controller — hält den Cluster-Zustand synchron mit dem Git-Repo, Details dazu weiter unten.
  • Sealed SecretsGeheimnisse werden verschlüsselt committet — kein Klartext-Passwort landet je in Git.
$argocd app list GitOps & Updates

Git ist die einzige Quelle der Wahrheit. Kein kubectl apply von Hand am Prod-Cluster — Änderungen laufen immer über den gleichen Weg:

1. Commit
Manifest ändern, pushen
2. ArgoCD sync
Erkennt den Diff, gleicht automatisch ab
3. Selbstheilung
Manuelle Cluster-Abweichungen werden zurückgedreht

Renovate hält die Container-Images aktuell — scannt alle Deployments, öffnet automatisch Pull-Requests bei neuen Versionen. Major-Updates werden ausgebremst, damit nichts über Nacht kaputtgeht.

$kubectl get pods -A | grep Running Was läuft drauf

Ein Auszug der selbst gehosteten Dienste — von Cloud-Ersatz bis Automatisierung:

17 Dienste · alle Running · Config vollständig in Git

Klick auf eine App für Aufbau & Besonderheiten.

$watch -n30 curl -s grafana/node-hardware-health Live-Status

Echtzeit-Metriken direkt aus dem Cluster — CPU, RAM, Disk und Temperatur aller drei Nodes, alle 30 Sekunden aktualisiert:

node-hardware-health.grafana

Dashboard in eigenem Tab öffnen ↗

Gebaut mit k3s, ArgoCD, Renovate, viel Kaffee und noch mehr MV-Landregen.