23 KiB
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
- Systemüberblick
- Architektur-Prinzipien
- Finales Netzwerk-Zielbild
- Zugangsmodell: Traefik vs. Tailscale
- Globale Sicherheitsregeln
- Einordnungsschema für neue Container
- Container-Zielbild (vollständig)
- Traefik-Label-Standard
- Migrationsstrategie (Blöcke A–F)
- Bekannte Ausnahmen und Begründungen
- Projektorganisation und Arbeitsmodus
- 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-Netzbackend_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änzenprivileged: truenur mit dokumentierter Begründungread_only: trueund 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:
homepagevaultwardenmealiepaperless-ngxgotifygitea(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:
PortainerCEDozzleUptimeKumadashdotfilebrowserscrutinyluckyBackupcode-serverscanopy-serverTraefik-DashboardnetdataGlancesnetalertx
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
- Keine produktiven Dienste im Docker-Default-
bridge - Keine direkten Host-Ports für Web-UIs außer dokumentierte Ausnahmen
restart: unless-stoppedals Standard- Secrets als Datei /
_FILE no-new-privileges:trueergänzen, wo praktikabeltraefik.docker.network=frontend_netimmer explizit setzen- Admin-Dienste immer mit
dashboard-auth@file,secure-headers@file - Placeholder-Domains (
yourdomain.tld) sind verboten privileged: truenur mit Begründung- Volume-Mounts so klein und so read-only wie möglich
- Neue Dienste nur via Compose Manager / YAML
- Ä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_netoder eigenes app-internes Netz - Nein → nur das funktional nötige Netz
Schritt 3 — Ist es eine Datenbank oder ein Cache?
- Ja → niemals
frontend_net, nurbackend_netoder 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 →
hostnur 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:
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.networkimmer explizit auffrontend_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 A–F)
Letzte Aktualisierung: 2026-03-23
Block A — Quick Wins (geringes Risiko, sofort)
[x] restart: unless-stopped für:
- Tailscale-Docker
- binhex-official-pihole
- postgresql17
- vaultwarden
- mail-archiver
- scrutiny
- netdata
- Stash
- Plex-Media-Server
[x] vaultwarden ADMIN_TOKEN-Doppelpräfix korrigieren
[x] backrest DNS-Hardcoding entfernen
[x] diun ↔ gotify Erreichbarkeit prüfen / herstellen
[x] leere Netzwerke prüfen und entfernen: br0, immich_net, kopia_default, netbox_default
[x] ungenutzte anonyme Volumes prüfen
Block B — Kritische Kernmigrationen (höchste Priorität)
[x] vaultwarden
- von bridge weg
- Host-Port weg
- Secret-Datei
- Traefik sauber aktiv
[x] postgresql17
- Port 5432 entfernen
- nur backend_net
- Secret-Datei
- aus bridge raus
[x] diun
- Join zu frontend_net
- gotify via Container-Name
[x] 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
- Sprint 1: Quick Wins +
vaultwarden - Sprint 2:
postgresql17+diun/gotify - Sprint 3:
mealie/mealie-postgres+mail-archiver - Sprint 4: Frontend-Stack (
paperless,Portainer,Dozzle,dashdot, etc.) - Sprint 5: Compose-Migration der Dockerman-Container
- Sprint 6: Hardening / Secrets / Volumes / Sonderfälle
11.3 Regel für jede Änderung
Jeder Sprint folgt demselben Schema:
- Zielbild in diesem Dokument prüfen
- nur den aktuellen Block anfassen
- Compose-Datei ändern
- deployen
- testen
- dokumentieren / abhaken
- erst dann nächster Schritt
11.4 Source-of-Truth-Hierarchie
- Dieses Dokument
- Compose-Dateien
- operative Checklisten / Excel
- 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_fileist 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
_FILEnicht
Regel
- Wenn
_FILEnicht 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_internalmealie-postgres→ nurmealie_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
- Backup erstellen (
pg_dumpall) - aktuellen Zustand sichern (
docker inspect) - neue Compose in Git definieren
- Container stoppen & entfernen
- neuen Stack deployen
- 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.