Files
homelab-infra/HOMELAB_ARCHITECTURE_MASTER_V2.md
T
2026-03-23 18:44:43 +00:00

21 KiB
Raw Blame History

HOMELAB_ARCHITECTURE — MASTER v2

Single Source of Truth für Docker-Netzwerkarchitektur, Sicherheitsregeln, Zielbild und Migration des Kallilabcore-Homelabs.
Arbeitsregel für KI-Assistenten: Dieses Dokument immer zuerst lesen, bevor Fragen zu Containern, Netzwerken, Traefik, Tailscale, Migration oder Security beantwortet werden.


Inhaltsverzeichnis

  1. Systemüberblick
  2. Architektur-Prinzipien
  3. Finales Netzwerk-Zielbild
  4. Zugangsmodell: Traefik vs. Tailscale
  5. Globale Sicherheitsregeln
  6. Einordnungsschema für neue Container
  7. Container-Zielbild (vollständig)
  8. Traefik-Label-Standard
  9. Migrationsstrategie (Blöcke AF)
  10. Bekannte Ausnahmen und Begründungen
  11. Projektorganisation und Arbeitsmodus
  12. Nutzung mit KI / Kontext-Regel

1. Systemüberblick

Eigenschaft Wert
Host-OS Unraid
Hostname Kallilabcore
Reverse Proxy Traefik v3
VPN / Remote-Zugang Tailscale (Tailscale-Docker, host-Netz)
DNS-Stack Pi-hole (host) → Unbound (dns_net)
Basis-Domain kaleschke.info
TLS Let's Encrypt via Cloudflare DNS Challenge
Certresolver le
Compose-Standard Unraid Compose Manager
Zusatz-Tool Portainer als Verwaltungs-UI
Homelab-Compose-Pfad /mnt/user/services/homelab/
Secrets-Pfad /mnt/user/appdata/secrets/
Grundsatz Keine neuen Dockerman-Einzelcontainer

2. Architektur-Prinzipien

P1 — Traefik ist der einzige öffentliche HTTP(S)-Einstiegspunkt

Kein Webdienst veröffentlicht final direkte Host-Ports außer traefik selbst.
Begründete Ausnahmen: gitea-SSH, Plex-Media-Server, binhex-official-pihole, Tailscale-Docker.

P2 — Das Setup bleibt bewusst einfach: frontend_net + backend_net + app-interne Netze

Die Quellenlage für Homelabs mit Traefik spricht für ein simples 2-Netz-Modell:

  • frontend_net = Proxy-/Web-Netz
  • backend_net = intern für DB/Cache/App-Kommunikation
  • zusätzliche Netze nur app-intern, wenn technisch nötig (mealie_internal, immich_default, scanopy_scanopy, dns_net)

Es gibt keine künstlichen globalen Zusatznetze wie admin_net, monitoring_net oder media_net.

P3 — Datenbanken gehören nie ins frontend_net

Postgres, Redis und ähnliche Dienste laufen ausschließlich in backend_net oder einem eigenen internen Compose-Netz.

P4 — Admin-UIs sind nicht öffentlich

Portainer, Dozzle, filebrowser, scrutiny, UptimeKuma, dashdot, code-server, luckyBackup, Traefik-Dashboard, netdata, Glances und netalertx sind standardmäßig Tailscale-only oder hinter Traefik mit zentraler Middleware abgesichert.

P5 — Compose-first

Alle produktiven Container werden als Compose verwaltet.
Bestehende Dockerman-/Ad-hoc-Container werden schrittweise migriert.

P6 — Secrets nie im Klartext

Passwörter, Tokens und API-Keys gehören in Secret-Dateien unter /mnt/user/appdata/secrets/ oder in _FILE-Variablen.
Ziel: kein sensibles Secret mehr sichtbar in docker inspect.

P7 — restart: unless-stopped ist Pflichtstandard

Jeder produktive Container nutzt restart: unless-stopped, außer eine Ausnahme ist dokumentiert.

P8 — Least Privilege

  • security_opt: ["no-new-privileges:true"] standardmäßig ergänzen
  • privileged: true nur mit dokumentierter Begründung
  • read_only: true und Non-Root nur nach getesteter Image-Kompatibilität
  • Docker-Socket standardmäßig vorsichtig behandeln; PortainerCE ist eine dokumentierte Ausnahme

3. Finales Netzwerk-Zielbild

3.1 Netz-Logik

Netzwerk Typ Zweck Status
frontend_net bridge, external einziges Traefik-/Web-Netz Standard
backend_net bridge, internal: true interne App-/DB-/Cache-Kommunikation Standard
dns_net bridge Resolver-Schicht für unbound bleibt
mealie_internal bridge, internal: true internes Netz nur für mealie + mealie-postgres Ziel
immich_default Compose-intern internes Immich-Netz bleibt
scanopy_scanopy Compose-intern internes Scanopy-Netz bleibt
diun_default Compose-intern Compose-Netz für diun bleibt, zusätzlich Join zu frontend_net
host host nur für echte Sonderfälle begründet

3.2 Finales Diagramm (vereinfacht)

Internet
   │
   ▼
traefik  (80/443)
   │
   └── frontend_net
         ├── öffentliche Apps
         ├── Admin-UIs mit Middleware
         └── interne Web-UIs für Tailscale-only

backend_net (internal: true)
   ├── postgresql17
   ├── Redis
   ├── mail-archiver
   └── paperless / andere Backends

App-interne Netze
   ├── mealie_internal
   ├── immich_default
   └── scanopy_scanopy

Host-Sonderfälle
   ├── Tailscale-Docker
   ├── binhex-official-pihole
   ├── Plex-Media-Server
   ├── netdata
   ├── Glances
   └── netalertx

3.3 Architekturentscheidung (final)

Wir bleiben final bei wenigen Netzen.
Das ist bewusst näher an aktuellen Homelab-Traefik-Setups als ein künstlich übersegmentiertes Docker-Netzmodell.
Trennung erfolgt primär über:

  • Traefik
  • Auth-/Security-Middlewares
  • Tailscale
  • kein direktes Port-Publishing
  • app-interne Netze
  • Host-Sonderfälle nur mit Begründung

4. Zugangsmodell: Traefik vs. Tailscale

4.1 Öffentlich über Traefik

Diese Dienste dürfen final über echte *.kaleschke.info-Domains erreichbar sein:

  • homepage
  • vaultwarden
  • mealie
  • paperless-ngx
  • gotify
  • gitea (Web)
  • immich_server
  • optional mail-archiver
  • optional backrest
  • optional Stash (nur wenn bewusst gewünscht)

4.2 Nicht öffentlich / nur Tailscale oder Traefik + Middleware

Diese Dienste sind keine Public Apps:

  • PortainerCE
  • Dozzle
  • UptimeKuma
  • dashdot
  • filebrowser
  • scrutiny
  • luckyBackup
  • code-server
  • scanopy-server
  • Traefik-Dashboard
  • netdata
  • Glances
  • netalertx

4.3 Regel

Wenn ein Dienst im frontend_net hängt, heißt das nicht automatisch öffentlich.
Admin-Dienste dürfen im frontend_net liegen, wenn:

  • Traefik sie routet
  • zentrale Middleware aktiv ist
  • keine direkten Host-Ports bestehen
  • Zugriff zusätzlich durch Tailscale bzw. Auth begrenzt ist

5. Globale Sicherheitsregeln

  1. Keine produktiven Dienste im Docker-Default-bridge
  2. Keine direkten Host-Ports für Web-UIs außer dokumentierte Ausnahmen
  3. restart: unless-stopped als Standard
  4. Secrets als Datei / _FILE
  5. no-new-privileges:true ergänzen, wo praktikabel
  6. traefik.docker.network=frontend_net immer explizit setzen
  7. Admin-Dienste immer mit dashboard-auth@file,secure-headers@file
  8. Placeholder-Domains (yourdomain.tld) sind verboten
  9. privileged: true nur mit Begründung
  10. Volume-Mounts so klein und so read-only wie möglich
  11. Neue Dienste nur via Compose Manager / YAML
  12. Änderungen immer gegen dieses Dokument prüfen

6. Einordnungsschema für neue Container

Schritt 1 — Hat der Dienst eine Web-UI?

  • Jafrontend_net
  • Nein → weiter zu Schritt 2

Schritt 2 — Braucht der Dienst eine DB / Redis / interne Backends?

  • Ja → zusätzlich backend_net oder eigenes app-internes Netz
  • Nein → nur das funktional nötige Netz

Schritt 3 — Ist es eine Datenbank oder ein Cache?

  • Ja → niemals frontend_net, nur backend_net oder internes Compose-Netz

Schritt 4 — Ist es ein Admin-/Monitoring-Dienst?

  • Ja → wenn Web-UI vorhanden trotzdem frontend_net, aber nur mit:
    • Middleware
    • keiner direkten Portfreigabe
    • Tailscale-only-Charakter

Schritt 5 — Braucht der Dienst Host-/Discovery-/L2-Sicht?

  • Jahost nur mit dokumentierter Begründung
  • Nein → kein host

Schritt 6 — Braucht die App ein eigenes internes App-Netz?

  • Ja → Compose-internes Netz mit internal: true
  • Nein → kein weiteres Netz anlegen

Kurzregel

  • UIfrontend_net
  • DB/Cachebackend_net
  • Spezialfall → app-internes Netz
  • Host-Zugriff → nur dokumentierte Ausnahme

7. Container-Zielbild (vollständig)

Legende Status:

  • = bereits weitgehend passend
  • = noch zu migrieren / zu korrigieren

7.1 Infrastruktur / Core

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
traefik frontend_net, backend_net öffentlich zentraler Ingress, 80/443 direkt Middleware-File-Provider sauber pflegen
homepage frontend_net Traefik öffentliche Startseite
ddns-updater backend_net intern kein Grund für frontend_net aus frontend_net raus
Tailscale-Docker host VPN-Zugang bleibt Host-Sonderfall restart fixen; TS_USERSPACE/privileged später prüfen, nicht als Quick Win
binhex-official-pihole host LAN DNS / intern bleibt Host-Sonderfall restart fixen
unbound dns_net intern Resolver bleibt isoliert
backrest frontend_net, backend_net Traefik oder intern UI via Traefik, Repo intern traefik.docker.network auf frontend_net; DNS-Hardcoding entfernen; Mounts straffen
diun diun_default, frontend_net intern braucht frontend_net, um gotify zu erreichen Gotify-Endpoint per Container-Name
theme-park frontend_net oder rein intern intern oder Traefik nur veröffentlichen, wenn wirklich genutzt Placeholder-Labels bereinigen
scanopy-daemon host, scanopy_scanopy falls nötig intern Host-naher Sonderfall Privileged nur dokumentiert belassen oder später testen

7.2 Sicherheit / Identity

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
vaultwarden frontend_net Traefik keine Host-Ports, kein bridge, Secret-Datei ADMIN_TOKEN-Bug fixen; Port 4743 entfernen; Compose-Migration

7.3 Datenbanken / Caches

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
postgresql17 backend_net intern keine Host-Ports, kein bridge Port 5432 entfernen; Secret-Datei; Compose-Migration
Redis backend_net intern intern-only Cache optional named volume statt anonym
mealie-postgres mealie_internal intern nur intern, nie frontend_net aus frontend_net raus; Secret-Datei
immich_postgres immich_default intern intern-only Passwort rotieren; named volume prüfen
immich_redis immich_default intern intern-only anonymes Volume perspektivisch bereinigen
scanopy-postgres scanopy_scanopy intern intern-only Passwort rotieren (aktuell Wiederverwendung)

7.4 Öffentliche Apps

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
paperless-ngx frontend_net, backend_net Traefik vorbereitete Labels final aktivieren traefik.enable=true; echte Domain; Port entfernen; Secret-Datei
Paperless-AI frontend_net Traefik oder intern Port nicht mehr direkt offen echte Domain; Labels aktivieren
mealie frontend_net, mealie_internal Traefik sauber getrennte App/DB-Struktur Port entfernen; Secret-Datei
gotify frontend_net Traefik oder intern intern per Container-Name für diun erreichbar Passwort rotieren; Port entfernen
gitea frontend_net Traefik + SSH Web via Traefik, SSH-Port bleibt HTTP-Labels sauberziehen
immich_server immich_default, frontend_net Traefik internes Immich-Netz bleibt; Web via Traefik Port 2283 entfernen; Secret-Datei; frontend_net ergänzen
immich_machine_learning immich_default intern bleibt intern
Stash frontend_net intern oder Traefik aus bridge raus; nur veröffentlichen wenn bewusst gewünscht Port entfernen; ggf. Traefik-Labels

7.5 Admin / Operations

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
code-server frontend_net Traefik + Middleware / Tailscale-only nicht öffentlich offen PASSWORDHASHED_PASSWORD; Secret-Datei
PortainerCE frontend_net Traefik + Middleware / Tailscale-only keine Host-Ports echte Domain; Labels aktivieren; direkte Ports entfernen; Docker-Socket nicht blind auf :ro
Dozzle frontend_net Traefik + Middleware / Tailscale-only keine Host-Ports Labels aktivieren; direkte Ports entfernen
filebrowser frontend_net Traefik + Middleware / Tailscale-only aus bridge; breite FS-Mounts prüfen Port entfernen; Mounts einschränken
scanopy-server scanopy_scanopy, frontend_net Traefik + Middleware / Tailscale-only kein direkter Host-Port frontend_net ergänzen; Port entfernen
luckyBackup frontend_net Traefik + Middleware / Tailscale-only aus bridge; nur intern Port entfernen; breite Mounts prüfen

7.6 Monitoring / Status

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
UptimeKuma frontend_net Traefik + Middleware / Tailscale-only keine direkten Ports Labels sauberziehen; Port entfernen
dashdot frontend_net Traefik + Middleware / Tailscale-only keine direkten Ports Placeholder-Domain raus; Labels aktivieren
Glances host Tailscale-only Host-Metriken optional später hinter Traefik, aber nicht nötig
netdata host Tailscale-only Host-Metriken restart fixen; leere CLAIM-Vars aufräumen
scrutiny frontend_net Traefik + Middleware / Tailscale-only aus bridge; echtes Image beibehalten Ports entfernen; später prüfen, ob privileged reduziert werden kann
netalertx host Tailscale-only Host-/Netzsicht bleibt optional später hinter Traefik, aber kein Muss

8. Traefik-Label-Standard

Jeder Dienst mit Traefik-Routing nutzt dieses Muster:

labels:
  - traefik.enable=true
  - traefik.docker.network=frontend_net
  - traefik.http.routers.<name>.rule=Host(`<subdomain>.kaleschke.info`)
  - traefik.http.routers.<name>.entrypoints=websecure
  - traefik.http.routers.<name>.tls=true
  - traefik.http.routers.<name>.tls.certresolver=le
  - traefik.http.services.<name>.loadbalancer.server.port=<interner-port>

Zusatz für Admin-Dienste

  - traefik.http.routers.<name>.middlewares=dashboard-auth@file,secure-headers@file

Regeln

  • traefik.docker.network immer explizit auf frontend_net
  • keine yourdomain.tld-Platzhalter
  • certresolver immer le
  • wenn Traefik aktiv ist, werden direkte Host-Ports entfernt
  • Admin-Dienste niemals ohne Middleware veröffentlichen

9. Migrationsstrategie (Blöcke AF)

Letzte Aktualisierung: 2026-03-23

Block A — Quick Wins (geringes Risiko, sofort)

[ ] restart: unless-stopped für:
    - Tailscale-Docker
    - binhex-official-pihole
    - postgresql17
    - vaultwarden
    - mail-archiver
    - scrutiny
    - netdata
    - Stash
    - Plex-Media-Server

[ ] vaultwarden ADMIN_TOKEN-Doppelpräfix korrigieren
[ ] backrest DNS-Hardcoding entfernen
[ ] diun ↔ gotify Erreichbarkeit prüfen / herstellen
[ ] leere Netzwerke prüfen und entfernen: br0, immich_net, kopia_default, netbox_default
[ ] ungenutzte anonyme Volumes prüfen

Block B — Kritische Kernmigrationen (höchste Priorität)

[ ] vaultwarden
    - von bridge weg
    - Host-Port weg
    - Secret-Datei
    - Traefik sauber aktiv

[ ] postgresql17
    - Port 5432 entfernen
    - nur backend_net
    - Secret-Datei
    - aus bridge raus

[ ] diun
    - Join zu frontend_net
    - gotify via Container-Name

[ ] mealie-postgres
    - aus frontend_net raus
    - mealie_internal

Block C — Frontend-Stack finalisieren

[ ] paperless-ngx
    - traefik.enable=true
    - paperless.kaleschke.info
    - Port entfernen

[ ] Paperless-AI
    - traefik.enable=true
    - paperless-ai.kaleschke.info
    - Port entfernen

[ ] PortainerCE
    - traefik.enable=true
    - portainer.kaleschke.info
    - Middleware
    - direkte Ports entfernen

[ ] Dozzle
    - traefik.enable=true
    - dozzle.kaleschke.info
    - Middleware
    - direkte Ports entfernen

[ ] dashdot
    - traefik.enable=true
    - dash.kaleschke.info
    - Middleware
    - direkte Ports entfernen

[ ] theme-park
    - nur wenn wirklich benötigt veröffentlichen

Block D — Bridge-/Dockerman-Container in Compose

[ ] vaultwarden
[ ] postgresql17
[ ] mail-archiver
[ ] scrutiny
[ ] filebrowser
[ ] luckyBackup
[ ] Stash
[ ] Tailscale-Docker
[ ] netdata
[ ] Plex-Media-Server
[ ] binhex-official-pihole

Block E — Secrets-Migration

[ ] vaultwarden       → ADMIN_TOKEN_FILE
[ ] postgresql17      → POSTGRES_PASSWORD_FILE
[ ] mail-archiver     → Authentication__Password_FILE
[ ] mealie            → POSTGRES_PASSWORD_FILE
[ ] mealie-postgres   → POSTGRES_PASSWORD_FILE
[ ] gotify            → Passwort rotieren + Secret-Datei
[ ] diun              → GOTIFY token als Datei
[ ] paperless-ngx     → PAPERLESS_DBPASS_FILE
[ ] code-server       → HASHED_PASSWORD / Secret
[ ] immich_server     → DB_PASSWORD rotieren + Datei
[ ] immich_postgres   → POSTGRES_PASSWORD_FILE
[ ] scanopy-postgres  → Passwort rotieren

Block F — Feinschliff / Hardening

[ ] backrest
    - traefik.docker.network → frontend_net
    - Mounts straffen

[ ] Redis
    - optional named volume

[ ] immich_redis
    - optional named volume

[ ] netdata
    - leere CLAIM-Vars entfernen

[ ] scrutiny
    - später prüfen, ob privileged reduziert werden kann

[ ] Tailscale-Docker
    - später prüfen, ob TS_USERSPACE/privileged bereinigt werden kann

[ ] Pi-hole
    - zuletzt konsolidieren, nicht als Erstprojekt

10. Bekannte Ausnahmen und Begründungen

Container Ausnahme Begründung
traefik Host-Ports 80/443 zentraler Reverse Proxy
Tailscale-Docker host, aktuell privileged, TS_USERSPACE=true bestehender VPN-Zugang; Umstellung nur kontrolliert
binhex-official-pihole host DNS-/DHCP-naher Sonderfall
Plex-Media-Server host Discovery / mDNS / Plex GDM
netdata host + zusätzliche Rechte Host-Metriken
Glances host Host-Metriken
netalertx host + Netzwerksicht ARP / Netzwerkscan
scanopy-daemon host, aktuell privilegiert hardware-/systemnaher Sonderfall
PortainerCE Docker-Socket nicht dogmatisch :ro Management-UI; Schreiboperationen können nötig sein

11. Projektorganisation und Arbeitsmodus

11.1 Unser Arbeitsprinzip

Dieses Projekt wird blockweise umgesetzt, nicht wild containerweise.

11.2 Reihenfolge der Umsetzung

  1. Sprint 1: Quick Wins + vaultwarden
  2. Sprint 2: postgresql17 + diun/gotify
  3. Sprint 3: mealie / mealie-postgres + mail-archiver
  4. Sprint 4: Frontend-Stack (paperless, Portainer, Dozzle, dashdot, etc.)
  5. Sprint 5: Compose-Migration der Dockerman-Container
  6. Sprint 6: Hardening / Secrets / Volumes / Sonderfälle

11.3 Regel für jede Änderung

Jeder Sprint folgt demselben Schema:

  1. Zielbild in diesem Dokument prüfen
  2. nur den aktuellen Block anfassen
  3. Compose-Datei ändern
  4. deployen
  5. testen
  6. dokumentieren / abhaken
  7. erst dann nächster Schritt

11.4 Source-of-Truth-Hierarchie

  1. Dieses Dokument
  2. Compose-Dateien
  3. operative Checklisten / Excel
  4. ad-hoc Notizen / Chat

12. Nutzung mit KI / Kontext-Regel

Wenn mit einer KI gearbeitet wird, gilt immer:

„Lies zuerst HOMELAB_ARCHITECTURE_MASTER_V2.md, dann beantworte meine Frage.“

Damit ist sofort klar:

  • welche Netze Standard sind
  • welche Container wohin gehören
  • welche Dienste öffentlich sein dürfen
  • welche Dienste nur intern/VPN-only sind
  • welche Migrationen noch offen sind
  • welche Ausnahmen bewusst dokumentiert sind

Schlussformel

Dieses Dokument ist keine lose Notiz, sondern das operative Masterdokument für die Docker- und Zugriffsarchitektur des Homelabs.

Zielbild in einem Satz:
frontend_net für alle Web-UIs, backend_net für interne Backends, app-interne Netze nur wenn technisch nötig, Tailscale für Remote-Admin-Zugriff, Traefik als einziger Web-Einstieg, keine produktiven bridge-Container mehr.