Files
homelab-infra/HOMELAB_ARCHITECTURE_MASTER_V2.md
T
Micha d362a9ab4c Aktualisierung
Aktualisierung meiner Doku
2026-04-15 12:21:02 +02:00

29 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.

Stand: 2026-04-15 | Aktueller Schwerpunkt: GitOps / Borg / Workflow-Konsolidierung


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
  13. Betriebserfahrungen und Entscheidungs-Log

1. Systemüberblick

Eigenschaft Wert
Host-OS Unraid
Hostname Kallilabcore
Reverse Proxy Traefik v3 (100% Docker-Labels, kein File-Provider)
VPN / Remote-Zugang Tailscale (tailscale, host-Netz, Git-Stack)
DNS-Stack AdGuard Home (dns_net + frontend_net) → Unbound (dns_net)
Basis-Domain kaleschke.info
TLS Let's Encrypt via Cloudflare DNS Challenge
Certresolver le
Compose-Standard Komodo (GitOps, Stack aus Gitea)
Legacy Portainer CE entfernt; Komodo ist alleiniger Stack-Manager
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 finale direkte Host-Ports außer traefik selbst. Begründete Ausnahmen: gitea-SSH (Port 222), AdGuard Home (Port 53/DNS + 3000/Admin), Tailscale, Plex-Media-Server.

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

  • 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_mealie_internal, immich_default, 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

Komodo, filebrowser, scrutiny, UptimeKuma, code-server, Traefik-Dashboard, backrest, borg-ui und beszel 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 als Komodo Stack Environment Variables mit ${VARIABLE} in der Compose.

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
  • Docker-Socket standardmäßig vorsichtig behandeln; Komodo ist 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: AdGuard Home + Unbound bleibt
mealie_mealie_internal bridge, internal: true internes Netz nur für mealie + mealie-postgres umgesetzt
immich_default Compose-intern, internal: true internes Immich-Netz umgesetzt
host host nur für echte Sonderfälle begründet

3.2 Finales Diagramm (vereinfacht)

Internet
│
▼
traefik (80/443)
│
└── frontend_net
    ├── öffentliche Apps (vaultwarden, mealie, paperless, immich, gitea, ntfy, homepage)
    ├── Admin-UIs mit Middleware (komodo, uptime-kuma, filebrowser, scrutiny, code-server, backrest, borg-ui, beszel)
    └── Hybrid-Dienste mit Internetbedarf (mail-archiver, ddns-updater)

backend_net (internal: true)
├── postgresql17
├── Redis
├── mail-archiver
└── paperless-ngx

dns_net
├── AdGuard Home (+ frontend_net, feste IP 172.23.0.3)
└── unbound

App-interne Netze
├── mealie_mealie_internal (internal: true) ✅
└── immich_default (internal: true) ✅

Host-Sonderfälle
├── tailscale
├── Plex-Media-Server
└── beszel-agent

4. Zugangsmodell: Traefik vs. Tailscale

4.1 Öffentlich über Traefik

Diese Dienste sind über echte *.kaleschke.info-Domains erreichbar:

  • vaultwarden — vault.kaleschke.info
  • mealie — mealie.kaleschke.info
  • paperless-ngx — paperless.kaleschke.info
  • ntfy — ntfy.kaleschke.info
  • gitea (Web) — git.kaleschke.info
  • immich_server — immich.kaleschke.info
  • homepage — homepage.kaleschke.info

4.2 Nicht öffentlich / nur Tailscale oder Traefik + Middleware

Diese Dienste sind keine Public Apps:

  • Komodo — komodo.kaleschke.info (Middleware)
  • UptimeKuma — uptime.kaleschke.info (Middleware)
  • filebrowser — files.kaleschke.info (Middleware)
  • scrutiny — scrutiny.kaleschke.info (Middleware)
  • code-server — Traefik + Middleware
  • beszel — beszel.kaleschke.info (Middleware ausstehend)
  • backrest — Traefik + Middleware
  • borg-ui — borg.kaleschke.info (Middleware)
  • Traefik-Dashboard
  • AdGuard Home — Port 3000 direkt (kein Traefik, nur LAN-Zugang)

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 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 oder Komodo Stack Environment Variables mit ${VAR}
  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 / Git-Stack
  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 externe Internetverbindungen?

  • Jafrontend_net (auch ohne Web-UI)
  • Nein → weiter zu Schritt 3

Schritt 3 — 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 4 — Ist es eine Datenbank oder ein Cache?

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

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

  • Ja → wenn Web-UI vorhanden trotzdem frontend_net, aber nur mit Middleware und ohne direkte Portfreigabe

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

  • Jahost nur mit dokumentierter Begründung

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

  • Ja → Compose-internes Netz mit internal: true

7. Container-Zielbild (vollständig)

Legende Status:

  • = umgesetzt und in Git-Stack
  • ✅ (Dockerman) = Traefik/Netz korrekt konfiguriert, noch kein Git-Stack
  • = noch zu migrieren / zu korrigieren
  • ⚠️ Legacy = läuft, wird abgelöst
  • = entfernt

7.1 Infrastruktur / Core

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
traefik frontend_net, backend_net öffentlich 80/443 zentraler Ingress, 100% Docker-Labels
AdGuard Home dns_net (172.23.0.3), frontend_net Port 53 DNS direkt, Port 3000 Admin (LAN) DNS-Server + Upstream zu unbound; kein Traefik (DNS-Sonderfall) Admin-Port per Traefik + Middleware absichern (Block F)
unbound dns_net intern Upstream-Resolver für AdGuard, isoliert
ddns-updater frontend_net intern Cloudflare DNS API; bleibt in frontend_net Dokumentierte Ausnahme
tailscale host VPN-Zugang Git-Stack (host-services/tailscale/) TS_USERSPACE/privileged später prüfen
backrest frontend_net, backend_net Traefik + Middleware traefik.docker.network=frontend_net korrigiert Breite Mounts straffen (Block F)
homepage frontend_net Traefik öffentliche Startseite via homepage.kaleschke.info

7.2 Sicherheit / Identity

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
vaultwarden frontend_net Traefik kein Host-Port, ADMIN_TOKEN_FILE
authelia 🔄 frontend_net, backend_net Traefik via auth.kaleschke.info ForwardAuth-Provider, Secrets via _FILE, PostgreSQL + Redis Shared NAS-seitige Einrichtung ausstehend (Secrets, DB, Users, DNS)

7.3 Datenbanken / Caches

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
postgresql17 backend_net intern kein Host-Port, POSTGRES_PASSWORD_FILE
Redis backend_net intern intern-only Cache optional named volume
mealie-postgres mealie_mealie_internal intern isoliert, nie frontend_net
immich_postgres immich_default intern intern-only
immich_redis immich_default intern intern-only anonymes Volume → named volume

7.4 Öffentliche Apps

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
paperless-ngx frontend_net, backend_net Traefik aktiv via paperless.kaleschke.info
Paperless-AI frontend_net Traefik aktiv
mealie frontend_net, mealie_mealie_internal Traefik sauber getrennte App/DB-Struktur
ntfy frontend_net Traefik aktiv via ntfy.kaleschke.info, Git-Stack
gitea frontend_net Traefik + SSH-Port 222 Web via Traefik, SSH direkt gebunden
immich_server immich_default, frontend_net Traefik aktiv via immich.kaleschke.info
immich_machine_learning immich_default intern bleibt intern

7.5 Admin / Operations

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
komodo frontend_net Traefik + Middleware primärer GitOps-Stack-Manager
code-server frontend_net Traefik + Middleware PASSWORD_FILE aktiv
PortainerCE entfernt - - 2026-03-29 abgeschaltet historisch; nicht mehr deployen
filebrowser frontend_net Traefik + Middleware aktiv via files.kaleschke.info Mounts einschränken (Block F)
borg-ui 🔄 frontend_net Traefik + Middleware Git-Stack für Borg/BorgBase-Backups; Borg UI bündelt Borg-CLI im Container BorgBase-SSH-Key hinterlegen, erstes Repo initialisieren, Quell-Mounts bei Bedarf gezielt erweitern
mail-archiver frontend_net, backend_net intern IMAP-Abruf + DB-Zugang, kein öffentlicher Zugang

7.6 Monitoring / Status

Container Status Soll-Netz(e) Finaler Zugang Finaler Sollzustand Offene Punkte
UptimeKuma frontend_net Traefik + Middleware aktiv via uptime.kaleschke.info
scrutiny frontend_net Traefik + Middleware aktiv via scrutiny.kaleschke.info, Git-Stack privileged später prüfen
beszel frontend_net Traefik aktiv via beszel.kaleschke.info, Git-Stack Admin-Middleware ergänzen (Block F)
beszel-agent host intern System-Monitoring, Socket-Zugriff auf Host

7.7 Sprint 5 — noch zu migrieren / abzuschalten

Container Status Ziel
Plex-Media-Server Dockerman Compose-Migration, host-Netz bleibt (Discovery)
PortainerCE abgeschlossen 2026-03-29 abgeschaltet

7.8 Entfernte Container

Container Entfernt am Begründung
scanopy-server 2026-03-26 nicht genutzt, durch paperless-ngx ersetzt
scanopy-postgres 2026-03-26 zusammen mit scanopy entfernt
scanopy-daemon 2026-03-26 zusammen mit scanopy entfernt
diun 2026-03-28 Update-Monitoring via Komodo; Stack + Netz diun_diun_default + Repo-Eintrag entfernt
binhex-official-pihole 2026-03-28 ersetzt durch AdGuard Home + Unbound
gotify 2026-03-28 nicht mehr aktiv; Push-Notifications via ntfy abgedeckt
Dozzle 2026-03-28 nicht mehr aktiv; Log-Monitoring via Komodo/beszel
dashdot 2026-03-28 nicht mehr aktiv; System-Monitoring via beszel
netdata 2026-03-28 nicht mehr aktiv; System-Monitoring via beszel
Glances 2026-03-28 nicht mehr aktiv
netalertx 2026-03-28 nicht mehr aktiv
luckyBackup 2026-03-28 nicht mehr aktiv; Backup via backrest
Stash 2026-03-28 nicht mehr aktiv
PortainerCE 2026-03-29 abgeschaltet; Komodo ist alleiniger Stack-Manager

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
  • tls=true immer explizit setzen
  • wenn Traefik aktiv ist, werden direkte Host-Ports entfernt
  • Admin-Dienste niemals ohne Middleware veröffentlichen
  • File-Provider nur noch für: middlewares.yml, tls.yml, dashboards.yml — keine Service-Routen mehr via File-Provider

9. Migrationsstrategie (Blöcke AF)

Letzte Aktualisierung: 2026-03-29

Block A — Quick Wins ABGESCHLOSSEN

[x] restart: unless-stopped für alle Container gesetzt
[x] vaultwarden ADMIN_TOKEN-Doppelpräfix korrigiert
[x] backrest DNS-Hardcoding entfernt
[x] leere Netzwerke entfernt: br0, immich_net, kopia_default, netbox_default, diun_default
[x] anonyme/verwaiste Volumes bereinigt
[x] scanopy komplett entfernt (3 Container + 2 Volumes + Netz)
[x] binhex-official-pihole entfernt → ersetzt durch AdGuard Home + Unbound

Block B — Kritische Kernmigrationen ABGESCHLOSSEN

[x] vaultwarden - frontend_net, Host-Port entfernt, ADMIN_TOKEN_FILE, Traefik aktiv
[x] postgresql17 - Port 5432 entfernt, nur backend_net, POSTGRES_PASSWORD_FILE
[x] mealie-postgres - aus frontend_net raus, nur mealie_mealie_internal

Block C — Frontend-Stack finalisieren ABGESCHLOSSEN

[x] ntfy - Git-Stack - ntfy.kaleschke.info - Traefik aktiv
[x] paperless-ngx - traefik.enable=true - paperless.kaleschke.info - Port entfernt - tls=true
[x] Paperless-AI - traefik.enable=true - aktiv
[x] PortainerCE - traefik.enable=true - Middleware aktiv - direkte Ports entfernt
[x] UptimeKuma - traefik.enable=true - uptime.kaleschke.info - Port entfernt - Middleware aktiv
[x] filebrowser - frontend_net - traefik.enable=true - files.kaleschke.info - Port entfernt - Middleware aktiv
[x] scrutiny - frontend_net - traefik.enable=true - scrutiny.kaleschke.info - Git-Stack
[x] gitea - traefik.enable=true - git.kaleschke.info - SSH-Port 222 bleibt (Ausnahme dokumentiert)
[x] backrest - traefik.docker.network=frontend_net korrigiert (war backend_net — Routing-Bug)
[x] Traefik File-Provider bereinigt - immich.yml, gitea.yml, mealie.yml, scrutiny.yml, vaultwarden.yml.bak gelöscht
[x] immich Bad Gateway behoben - Traefik nutzt jetzt immich@docker statt immich@file
[x] AdGuard Home - Git-Stack - dns_net + frontend_net - Port 53 (DNS) + 3000 (Admin)
[x] beszel - Git-Stack - frontend_net - beszel.kaleschke.info - Traefik aktiv
[ ] beszel - Admin-Middleware (dashboard-auth@file) ergänzen

Block D — Dockerman-Container in Git-Stacks

[x] vaultwarden ✅
[x] postgresql17 ✅
[x] mail-archiver ✅
[x] scrutiny ✅
[x] filebrowser ✅
[x] tailscale ✅
[x] AdGuard Home ✅
[x] beszel ✅
[x] ntfy ✅
[x] homepage ✅
[ ] Plex-Media-Server (Sprint 5)

Block E — Secrets-Migration

[x] vaultwarden → ADMIN_TOKEN_FILE ✅
[x] postgresql17 → POSTGRES_PASSWORD_FILE ✅
[x] mail-archiver → Stack ENV (${MAILARCHIVER_AUTH_PASSWORD}) ✅
[x] mealie → Stack ENV (kein _FILE-Support) ✅
[x] mealie-postgres → Stack ENV (kein _FILE-Support) ✅
[x] paperless-ngx → Stack ENV (${PAPERLESS_DBPASS}) ✅
[x] code-server → PASSWORD_FILE ✅
[x] immich_server → Stack ENV (${IMMICH_DB_PASSWORD}) ✅
[x] immich_postgres → POSTGRES_PASSWORD_FILE ✅
[ ] immich_redis → anonymes Volume → named volume

Block F — Feinschliff / Hardening

[x] immich_default - internal: true gesetzt (2026-03-29)
[x] PortainerCE - abgeschaltet (Sprint 5, 2026-03-29)
[ ] immich_redis - anonymes Volume → named volume in Compose
[ ] immich_server - anonymes Volume prüfen und benennen
[ ] backrest - /mnt/user doppelt gemountet (ro + rw) - rw-Mount auf konkrete Pfade einschränken
[ ] filebrowser - /mnt/user:/srv ist sehr breit - auf /mnt/user/documents:/srv einschränken wenn möglich
[ ] Redis - optional named volume
[ ] scrutiny - später prüfen, ob privileged reduziert werden kann
[ ] tailscale - TS_USERSPACE/privileged bereinigen wenn möglich
[ ] beszel - Admin-Middleware (dashboard-auth@file) ergänzen
[ ] AdGuard Home - Admin-Port 3000 per Traefik + Middleware absichern (aktuell direkter Port)

Block G — Authelia SSO/2FA (Sprint 7)

[x] security/authelia/docker-compose.yml im Repo (2026-03-29)
[x] security/authelia/configuration.yml im Repo (2026-03-29)
[x] traefik/dynamic/middlewares.yml - authelia ForwardAuth Middleware ergänzt (2026-03-29)
[ ] NAS: Secrets anlegen (jwt_secret, session_secret, storage_encryption_key, postgres_password)
[ ] NAS: Authelia PostgreSQL-User und -Datenbank anlegen
[ ] NAS: /mnt/user/appdata/authelia/config/configuration.yml aus Repo übernehmen
[ ] NAS: users_database.yml mit gehashten Passwörtern anlegen
[ ] NAS: DNS-Eintrag auth.kaleschke.info in AdGuard setzen
[ ] Komodo: Stack security/authelia deployen
[ ] Services schrittweise mit authelia@docker Middleware absichern

10. Bekannte Ausnahmen und Begründungen

Container Ausnahme Begründung
traefik Host-Ports 80/443 zentraler Reverse Proxy
tailscale host VPN-Zugang; Umstellung nur kontrolliert möglich
AdGuard Home Port 53 (TCP/UDP) direkt + Port 3000 Admin DNS benötigt direkten Port 53; kein HTTP-Proxy für DNS möglich
Plex-Media-Server host Discovery / mDNS / Plex GDM
scrutiny privileged: true SMART-Datenzugriff auf Laufwerke
beszel-agent host direkter Host-Zugriff für System-Monitoring nötig
Komodo Docker-Socket Zugriff Stack-Deployments benötigen Socket
gitea SSH-Port 222 direkt gebunden Git-SSH-Zugang; kein HTTP-Proxy für SSH möglich
ddns-updater bleibt in frontend_net statt backend_net braucht Cloudflare-API-Zugang; backend_net ist internal: true
mail-archiver frontend_net + backend_net braucht Internetzugang für IMAP-Abruf (GMX, Gmail) und DB-Zugang

11. Projektorganisation und Arbeitsmodus

11.1 Unser Arbeitsprinzip

Dieses Projekt wird blockweise umgesetzt, nicht wild containerweise.

11.2 Reihenfolge der Umsetzung

Sprint Inhalt Status
Sprint 1 Quick Wins + vaultwarden Abgeschlossen
Sprint 2 postgresql17 + diun/gotify Abgeschlossen
Sprint 3 mealie / mealie-postgres + mail-archiver Abgeschlossen
Sprint 4 Frontend-Stack (paperless, PortainerCE, Dozzle, dashdot, scrutiny, filebrowser, gitea, UptimeKuma, ntfy, beszel) + Traefik File-Provider Bereinigung + Komodo Einführung + AdGuard Home Migration + Pi-hole Ablösung Abgeschlossen
Sprint 5 Plex-Media-Server Compose-Migration + PortainerCE abschalten Abgeschlossen
Sprint 6 Hardening / Secrets / Volumes / Sonderfälle (immich_default , Volumes, Mounts, AdGuard Traefik) Abgeschlossen
Sprint 7 Authelia SSO/2FA: Compose + Config im Repo, Traefik ForwardAuth Middleware, NAS-seitige Einrichtung 🔄 In Bearbeitung
Sprint 8 borg-ui Git-Stack + BorgBase Offsite-Backup-Workflow 🔄 In Bearbeitung

11.3 Regel für jede Änderung

  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. Gitea Online (origin/master)
  2. lokaler Clone / GitHub Desktop
  3. Compose-Dateien im Git-Repo
  4. Komodo als Deploy-Consumer
  5. operative Checklisten und Notizen

11.5 Operativer Git-Workflow

  • Gitea Online ist der verbindliche Sollzustand.
  • Lokal wird standardmäßig über GitHub Desktop gearbeitet.
  • Komodo deployt aus Gitea und ist kein Bearbeitungsort.
  • Webhooks sind aktiv: Ein Push kann unmittelbar einen Komodo-Deploy auslösen.
  • Wenn online in Gitea editiert wurde, muss vor der nächsten lokalen Änderung zuerst Fetch origin und danach Pull origin erfolgen.

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

13. Betriebserfahrungen und Entscheidungs-Log

Traefik — Wechsel zu reinen Docker-Labels (2026-03-28)

Die statischen File-Provider-Konfigurationen in /mnt/user/appdata/traefik/dynamic/ wurden vollständig bereinigt:

  • Gelöscht: immich.yml, gitea.yml, mealie.yml, scrutiny.yml, vaultwarden.yml.bak
  • Verbleibend (notwendig): middlewares.yml, tls.yml, dashboards.yml

Hintergrund: Die alten File-Provider-Configs haben @file-Routen mit @docker-Routen konkurrieren lassen. In Traefik v3 gewinnt der File-Provider und hat z.B. Immich auf die falsche IP geroutet (Bad Gateway). Nach Löschung läuft Traefik ausschließlich auf Docker-Labels.

Regel: Neue Dienste ausschließlich via Docker Compose Labels konfigurieren. Keine neuen .yml-Dateien im dynamic/-Verzeichnis für Service-Routen anlegen.

Komodo — Ablösung von Portainer als Stack-Manager (2026-03-28)

Komodo ist nun der primäre GitOps-Stack-Manager:

  • Komodo Core läuft als Docker-Stack (ops/komodo/docker-compose.yml)
  • Komodo Periphery läuft auf dem Unraid-Host für direktes Server-Management
  • Stacks werden via Gitea synchronisiert und über Komodo deployed
  • Portainer CE ist abgeschaltet; Komodo ist der alleinige aktive Stack-Manager

Betriebsregel: Alle Stack-Änderungen laufen über Git; Komodo konsumiert nur den Stand aus Gitea.

AdGuard Home — Ablösung von Pi-hole (2026-03-28)

binhex-official-pihole wurde entfernt und durch AdGuard Home + unbound ersetzt:

  • AdGuard läuft als Git-Stack (host-services/Adguard/docker-compose.yml)
  • Netzwerke: dns_net (feste IP 172.23.0.3) + frontend_net
  • Port 53 (DNS) direkt gebunden — dokumentierte Ausnahme
  • Port 3000 (Admin-UI) direkt gebunden — Traefik-Absicherung ausstehend (Block F)
  • unbound läuft weiterhin als Upstream-Resolver in dns_net

diun — Entfernung (2026-03-28)

diun (Docker Image Update Notifier) wurde deinstalliert:

  • Stack gelöscht
  • Orphan-Netzwerk diun_diun_default bereinigt
  • Repo-Eintrag infra/diun/ aus Git entfernt

Update-Monitoring kann über Komodo's eingebaute Update-Notifications abgedeckt werden.

ntfy — Push-Notifications (Git-Stack)

ntfy läuft als Git-Stack (apps/ntfy/docker-compose.yml):

  • ntfy.kaleschke.info via Traefik
  • NTFY_UPSTREAM_BASE_URL: https://ntfy.sh für mobile Push-Notifications
  • NTFY_BEHIND_PROXY: true korrekt gesetzt

immich_default — internal: true gesetzt (2026-03-29)

immich_default wurde von external: true auf ein Compose-verwaltetes internes Netz umgestellt:

  • Vorher: external: true (manuell erstellt, falsche Labels com.docker.compose.network=default)
  • Nachher: Compose-managed, internal: true, driver: bridge, korrekte Labels
  • Durchgeführt via: manuelles docker stop der Containers → docker network rm immich_default → Komodo Redeploy
  • Ergebnis: alle Immich-Container (immich_postgres, immich_redis, immich_machine_learning) sind jetzt vom Internet isoliert; nur immich_server hat zusätzlich frontend_net für Traefik

Secrets in Komodo / Portainer Stacks

Host-Pfade in env_file (z.B. /mnt/...) sind in Git-Stacks nicht verfügbar. Standardlösung: Stack Environment Variables + ${VARIABLE_NAME} in der Compose.

Regel: Wenn _FILE nicht unterstützt wird → Stack Environment Variable. Kein Secret im Git.

Borg UI / BorgBase (2026-04-12)

  • borg-ui läuft als Admin-Dienst in ops/borg-ui/docker-compose.yml
  • nur frontend_net, weil Web-UI + externer SSH-Zugang zu BorgBase benötigt werden
  • keine direkten Host-Ports; Zugriff ausschließlich via Traefik + Middleware über borg.kaleschke.info
  • Mounts bewusst klein gehalten: /mnt/user/appdata read-only als erste Backup-Quelle, separates Restore-Ziel unter /mnt/user/appdata/borg-ui/restore
  • kein separater Borg-CLI-Container nötig, da Borg UI die Borg-CLI bereits im Container mitbringt
Container _FILE Support
Vaultwarden ja
PostgreSQL ja
code-server ja (PASSWORD_FILE)
Immich Postgres ja (POSTGRES_PASSWORD_FILE)
Mealie nein → Stack ENV
paperless-ngx nein für DB-Pass → Stack ENV

ddns-updater — Netz-Ausnahme

Bleibt bewusst in frontend_net statt backend_net, weil backend_net internal: true ist und ddns-updater die Cloudflare-API erreichen muss.

mail-archiver — Hybrid-Dienst

Benötigt backend_net (PostgreSQL) + frontend_net (IMAP-Abruf von GMX/Gmail). Kein reiner Backend-Dienst. Kein öffentlicher Traefik-Zugang.

Netzwerk-Standard für Apps mit Datenbanken

  • App → frontend_net + internes Netzwerk
  • Datenbank → nur internes Netzwerk (internal: true)

Beispiel (Mealie): mealiefrontend_net + mealie_mealie_internal, mealie-postgres → nur mealie_mealie_internal.


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 und Dienste mit Internetbedarf, 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 (100% Docker-Labels), Komodo als GitOps-Stack-Manager, AdGuard Home + Unbound für DNS, keine produktiven bridge-Container mehr.