Files
homelab-infra/HOMELAB_ARCHITECTURE_MASTER_V2.md
T

642 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](#1-systemüberblick)
2. [Architektur-Prinzipien](#2-architektur-prinzipien)
3. [Finales Netzwerk-Zielbild](#3-finales-netzwerk-zielbild)
4. [Zugangsmodell: Traefik vs. Tailscale](#4-zugangsmodell-traefik-vs-tailscale)
5. [Globale Sicherheitsregeln](#5-globale-sicherheitsregeln)
6. [Einordnungsschema für neue Container](#6-einordnungsschema-für-neue-container)
7. [Container-Zielbild (vollständig)](#7-container-zielbild-vollständig)
8. [Traefik-Label-Standard](#8-traefik-label-standard)
9. [Migrationsstrategie (Blöcke AF)](#9-migrationsstrategie-blöcke-a-f)
10. [Bekannte Ausnahmen und Begründungen](#10-bekannte-ausnahmen-und-begründungen)
11. [Projektorganisation und Arbeitsmodus](#11-projektorganisation-und-arbeitsmodus)
12. [Nutzung mit KI / Kontext-Regel](#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)
```text
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?
- **Ja** → `frontend_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?
- **Ja** → `host` 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
- **UI** → `frontend_net`
- **DB/Cache** → `backend_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 | `PASSWORD``HASHED_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:
```yaml
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
```yaml
- 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)
```text
[ ] 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)
```text
[ ] 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
```text
[ ] 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
```text
[ ] vaultwarden
[ ] postgresql17
[ ] mail-archiver
[ ] scrutiny
[ ] filebrowser
[ ] luckyBackup
[ ] Stash
[ ] Tailscale-Docker
[ ] netdata
[ ] Plex-Media-Server
[ ] binhex-official-pihole
```
### Block E — Secrets-Migration
```text
[ ] 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
```text
[ ] 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
---
## ⚠️ Erweiterte Learnings (Portainer + GitOps)
### Secrets in Portainer Git-Stacks
Bei Deployments über Portainer mit Git-Repositories gilt:
* Host-Pfade in `env_file` (z. B. `/mnt/...`) sind **nicht verfügbar**
* Portainer-Container hat keinen Zugriff auf diese Pfade
#### Konsequenz
* `env_file` ist für Secrets ungeeignet in diesem Setup
#### Standardlösung
* Verwendung von **Portainer Environment Variables**
* Compose nutzt Variablen:
* `POSTGRES_PASSWORD: ${VARIABLE_NAME}`
👉 Ergebnis:
* keine Secrets im Git
* funktionierender Git-Deployment-Flow
---
### Umgang mit `_FILE` Variablen
Nicht alle Container unterstützen `_FILE`-basierte Secrets.
#### Übersicht
* Vaultwarden → unterstützt `_FILE`
* PostgreSQL → unterstützt `_FILE`
* Mealie → unterstützt `_FILE` **nicht**
#### Regel
* Wenn `_FILE` nicht unterstützt wird:
→ Nutzung von Environment Variables über Portainer
---
### Netzwerk-Standard für Apps mit Datenbanken
#### Architektur-Regel
* App → `frontend_net` + internes Netzwerk
* Datenbank → nur internes Netzwerk (`internal: true`)
#### Beispiel (Mealie)
* `mealie``frontend_net` + `mealie_internal`
* `mealie-postgres` → nur `mealie_internal`
👉 Vorteil:
* Datenbank vollständig isoliert
* keine externe Erreichbarkeit möglich
---
### Migrations-Standard für kritische Services
Bei Änderungen an Datenbanken oder Core-Services:
#### Vorgehen
1. Backup erstellen (`pg_dumpall`)
2. aktuellen Zustand sichern (`docker inspect`)
3. neue Compose in Git definieren
4. Container stoppen & entfernen
5. neuen Stack deployen
6. Funktion prüfen (Logs + abhängige Services)
#### Grundsatz
* Daten liegen im Volume → bleiben erhalten
* Container sind austauschbar
👉 Ziel:
* risikoarme Migration ohne Datenverlust
## 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.