chore: finish postgres redis stateful migrations

This commit is contained in:
2026-05-31 20:32:25 +02:00
parent 80a5ad24a2
commit 268df30a13
25 changed files with 198 additions and 107 deletions
+4 -3
View File
@@ -43,7 +43,7 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: redis:7.4-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99 image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1
restart: unless-stopped restart: unless-stopped
networks: networks:
- immich_default - immich_default
@@ -52,14 +52,15 @@ services:
database: database:
container_name: immich_postgres container_name: immich_postgres
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
restart: unless-stopped restart: unless-stopped
environment: environment:
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
POSTGRES_USER: immich POSTGRES_USER: immich
POSTGRES_DB: immich POSTGRES_DB: immich
shm_size: 128mb
volumes: volumes:
- /mnt/user/appdata/immich_postgres:/var/lib/postgresql/data - /mnt/user/appdata/immich_postgres_vectorchord:/var/lib/postgresql/data
- /mnt/user/appdata/secrets/immich_postgres_password.txt:/run/secrets/postgres_password:ro - /mnt/user/appdata/secrets/immich_postgres_password.txt:/run/secrets/postgres_password:ro
networks: networks:
- immich_default - immich_default
+3 -3
View File
@@ -38,7 +38,7 @@ services:
- traefik.http.services.mealie.loadbalancer.server.port=9000 - traefik.http.services.mealie.loadbalancer.server.port=9000
mealie-postgres: mealie-postgres:
image: postgres:17.10@sha256:0027bef26712baaee437a4ea48fdf3d2d2e2bc5f0d81615374408ca320f3c7e3 image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39
container_name: mealie-postgres container_name: mealie-postgres
restart: unless-stopped restart: unless-stopped
@@ -47,10 +47,10 @@ services:
POSTGRES_USER: mealie POSTGRES_USER: mealie
POSTGRES_DB: mealie POSTGRES_DB: mealie
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
PGDATA: /var/lib/postgresql/data PGDATA: /var/lib/postgresql/18/docker
volumes: volumes:
- /mnt/user/appdata/mealie/postgres:/var/lib/postgresql/data - /mnt/user/appdata/mealie/postgres18:/var/lib/postgresql
- /mnt/user/appdata/secrets/mealie_postgres_password.txt:/run/secrets/postgres_password:ro - /mnt/user/appdata/secrets/mealie_postgres_password.txt:/run/secrets/postgres_password:ro
networks: networks:
+4 -4
View File
@@ -46,7 +46,7 @@ services:
- "traefik.http.services.nextcloud.loadbalancer.server.port=80" - "traefik.http.services.nextcloud.loadbalancer.server.port=80"
nextcloud-postgres: nextcloud-postgres:
image: postgres:17.10@sha256:0027bef26712baaee437a4ea48fdf3d2d2e2bc5f0d81615374408ca320f3c7e3 image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39
container_name: nextcloud-postgres container_name: nextcloud-postgres
restart: unless-stopped restart: unless-stopped
environment: environment:
@@ -54,9 +54,9 @@ services:
POSTGRES_DB: nextcloud POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud POSTGRES_USER: nextcloud
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
PGDATA: /var/lib/postgresql/data PGDATA: /var/lib/postgresql/18/docker
volumes: volumes:
- /mnt/user/appdata/nextcloud/postgres:/var/lib/postgresql/data - /mnt/user/appdata/nextcloud/postgres18:/var/lib/postgresql
- /mnt/user/appdata/secrets/nextcloud_postgres_password.txt:/run/secrets/postgres_password:ro - /mnt/user/appdata/secrets/nextcloud_postgres_password.txt:/run/secrets/postgres_password:ro
networks: networks:
- nextcloud_internal - nextcloud_internal
@@ -64,7 +64,7 @@ services:
- no-new-privileges:true - no-new-privileges:true
nextcloud-redis: nextcloud-redis:
image: redis:7.4-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99 image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1
container_name: nextcloud-redis container_name: nextcloud-redis
restart: unless-stopped restart: unless-stopped
command: redis-server --save 60 1 --loglevel warning command: redis-server --save 60 1 --loglevel warning
+51 -1
View File
@@ -261,7 +261,7 @@ Ziel:
### Stufe 2 - Gemeinsame Backends und Identity ### Stufe 2 - Gemeinsame Backends und Identity
4. `infra/postgresql17/` 4. `infra/postgresql17/` (PostgreSQL 18 runtime, historischer Stack-Name bleibt fuer Service-DNS stabil)
5. `security/authelia/` 5. `security/authelia/`
6. `infra/redis/` 6. `infra/redis/`
7. `core/gitea/` 7. `core/gitea/`
@@ -396,6 +396,12 @@ Vor dem Start muessen vorhanden sein:
Zusaetzlich muss der Nutzdatenpfad `/mnt/user/documents/nextcloud-data` erreichbar sein. Zusaetzlich muss der Nutzdatenpfad `/mnt/user/documents/nextcloud-data` erreichbar sein.
Beim PostgreSQL-Restore beachten:
- vor einem produktiven Dump `occ maintenance:mode --on` setzen
- die produktive DB-Rolle kann von `POSTGRES_USER` abweichen; aktuell nutzt Nextcloud laut `config.php` die Rolle `oc_admin`
- nach Restore und erfolgreichem `occ status` den Wartungsmodus mit `occ maintenance:mode --off` beenden
### Borg-Dumps ### Borg-Dumps
Die Dump-Erzeugung ist host-seitig gedacht, nicht als Borg-UI-Inline-Hook. Die Dump-Erzeugung ist host-seitig gedacht, nicht als Borg-UI-Inline-Hook.
@@ -406,6 +412,50 @@ Relevant:
- Skript: `ops/borg-ui/scripts/pre-backup-dumps.sh` - Skript: `ops/borg-ui/scripts/pre-backup-dumps.sh`
- Unraid-Flash-Artefakt: `unraid-flash-config.tar.gz` plus `.sha256` und Manifest im selben Zielpfad - Unraid-Flash-Artefakt: `unraid-flash-config.tar.gz` plus `.sha256` und Manifest im selben Zielpfad
### Redis 8 Restore / Rollback
Redis-Instanzen laufen auf der 8.x-Schiene. Vor Major-Upgrades wird `redis-cli SAVE` ausgefuehrt und der jeweilige Datenpfad kopiert.
Aktive Pfade und Besonderheiten:
- Shared Redis: `/mnt/user/appdata/redis`, Passwort aus `redis_password.txt`, AOF aktiv.
- Nextcloud Redis: `/mnt/user/appdata/nextcloud/redis`, ohne Redis-Passwort, Snapshot-Persistenz.
- Immich Redis: cache/queue-only ohne bind-mounted Datenpfad; Restore-Wahrheit ist Immich Postgres + Foto-Dateien, nicht Redis.
Rollback:
1. Abhaengige App stoppen.
2. Redis stoppen.
3. Compose auf das vorherige Redis-7.4-Image zuruecksetzen.
4. Bei Shared/Nextcloud den vor dem Cutover kopierten Datenpfad zurueckkopieren.
5. Redis und App starten, `redis-cli INFO server` und App-Smoke pruefen.
### PostgreSQL 18 Major-Upgrade / Rollback
Produktive PostgreSQL-18-Cluster verwenden das Docker-Image-Layout mit Host-Mount auf `/var/lib/postgresql` und `PGDATA=/var/lib/postgresql/18/docker`.
Aktive Datenpfade:
- Shared PostgreSQL: `/mnt/user/appdata/postgresql18`
- Mealie PostgreSQL: `/mnt/user/appdata/mealie/postgres18`
- Nextcloud PostgreSQL: `/mnt/user/appdata/nextcloud/postgres18`
Rollback-Altstaende, bis zur separaten Loeschfreigabe nicht entfernen:
- Shared PostgreSQL 17: `/mnt/user/appdata/postgresql17`
- Mealie PostgreSQL 17: `/mnt/user/appdata/mealie/postgres`
- Nextcloud PostgreSQL 17: `/mnt/user/appdata/nextcloud/postgres`
Restore-Reihenfolge fuer den Shared-Cluster:
1. Frischen PostgreSQL-18-Cluster starten.
2. Globals aus `pg_dumpall --globals-only` einspielen.
3. Den bekannten Bootstrap-Konflikt fuer `CREATE ROLE mailarchiver;` gezielt tolerieren bzw. herausfiltern, danach `ALTER ROLE mailarchiver ...` dennoch einspielen.
4. Datenbanken anlegen und Custom-Format-Dumps mit `pg_restore` einspielen.
5. Restore-Logs auf echte `ERROR`, `FATAL` und `PANIC` pruefen.
Immich ist bewusst nicht Teil dieses PostgreSQL-18-Laufs: Die produktive DB bleibt auf PostgreSQL 14 und nutzt das Immich-Postgres-Image mit VectorChord/pgvector. VectorChord-Backups brauchen zum Restore ein Image mit VectorChord; der alte pgvecto.rs-Datenpfad `/mnt/user/appdata/immich_postgres` bleibt bis zur separaten Loeschfreigabe als Rollback-Altstand erhalten.
### Hermes Agent ### Hermes Agent
Hermes nutzt einen lokalen Build und hostseitige Runtime-Daten. Hermes nutzt einen lokalen Build und hostseitige Runtime-Daten.
+5 -5
View File
@@ -11,7 +11,7 @@ Schliesst die Audit-Luecke aus F-11: Immich ist der groesste Datentopf (Familien
| Datei | Zweck | | Datei | Zweck |
|---|---| |---|---|
| `ops/restore-tests/immich-compose.test.yml` | isoliertes Test-Compose: pgvecto-rs Postgres + Redis + Immich-Server, ML weggelassen, `127.0.0.1:12283` | | `ops/restore-tests/immich-compose.test.yml` | isoliertes Test-Compose: Immich-Postgres mit VectorChord + Redis + Immich-Server, ML weggelassen, `127.0.0.1:12283` |
| `ops/restore-tests/immich-restore-test.sh` | Host-Bash-Skript fuer den Lauf, mit `--what-if` und `--keep-data` Flags | | `ops/restore-tests/immich-restore-test.sh` | Host-Bash-Skript fuer den Lauf, mit `--what-if` und `--keep-data` Flags |
| `ops/restore-tests/immich-restore-test.ps1` | Plan-Scaffold fuer Windows-Operator-Sicht (kein Live-Run) | | `ops/restore-tests/immich-restore-test.ps1` | Plan-Scaffold fuer Windows-Operator-Sicht (kein Live-Run) |
| `ops/restore-tests/immich-plan.md` | Fachlicher Plan: Quellen, Schutzregeln, Smoke-Test-Kriterien, bekannte Risiken | | `ops/restore-tests/immich-plan.md` | Fachlicher Plan: Quellen, Schutzregeln, Smoke-Test-Kriterien, bekannte Risiken |
@@ -20,7 +20,7 @@ Schliesst die Audit-Luecke aus F-11: Immich ist der groesste Datentopf (Familien
## Was der Test abdeckt ## Was der Test abdeckt
- Extraktion von `local/borg-dumps/latest/immich.dump` aus dem aktuellsten Borg-Archiv - Extraktion von `local/borg-dumps/latest/immich.dump` aus dem aktuellsten Borg-Archiv
- Import in eine isolierte `tensorchord/pgvecto-rs:pg14-v0.2.0` Postgres-Instanz mit demselben Digest wie Produktion - Import in eine isolierte `ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0` Postgres-Instanz mit demselben Digest wie Produktion
- Start eines isolierten Immich-Server-Containers mit demselben Digest wie Produktion, **ohne** ML-Container und **ohne** Traefik - Start eines isolierten Immich-Server-Containers mit demselben Digest wie Produktion, **ohne** ML-Container und **ohne** Traefik
- Smoke-Test: Login-Seite erreichbar, `asset`- und `"user"`-Tabelle lesbar - Smoke-Test: Login-Seite erreichbar, `asset`- und `"user"`-Tabelle lesbar
- Markdown-Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md` - Markdown-Report unter `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md`
@@ -57,7 +57,7 @@ Der Test deckt **Stufe 4 (kritische Anwendungen)** aus `docs/DISASTER_RECOVERY.m
## Schutzregeln ## Schutzregeln
- Skript greift ausschliesslich auf den Restore-Lab-Pfad und den Borg-Extract-Cache zu. - Skript greift ausschliesslich auf den Restore-Lab-Pfad und den Borg-Extract-Cache zu.
- Produktive Pfade unter `/mnt/user/photos/*` und `/mnt/user/appdata/immich_postgres/` werden nicht angefasst. - Produktive Pfade unter `/mnt/user/photos/*`, `/mnt/user/appdata/immich_postgres_vectorchord/` und dem Rollback-Altstand `/mnt/user/appdata/immich_postgres/` werden nicht angefasst.
- Produktive Container `immich_server`, `immich_postgres`, `immich_redis`, `immich_machine_learning` werden nicht gestoppt, nicht beruehrt. - Produktive Container `immich_server`, `immich_postgres`, `immich_redis`, `immich_machine_learning` werden nicht gestoppt, nicht beruehrt.
- Borg-Passphrase wird aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` gelesen und nicht in Reports, Logs oder Doku geschrieben. - Borg-Passphrase wird aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` gelesen und nicht in Reports, Logs oder Doku geschrieben.
- Test-Container publishen nur auf `127.0.0.1:12283`, nicht auf LAN- oder Tailscale-Interface. - Test-Container publishen nur auf `127.0.0.1:12283`, nicht auf LAN- oder Tailscale-Interface.
@@ -66,8 +66,8 @@ Der Test deckt **Stufe 4 (kritische Anwendungen)** aus `docs/DISASTER_RECOVERY.m
## Risiken (aus `ops/restore-tests/immich-plan.md`) ## Risiken (aus `ops/restore-tests/immich-plan.md`)
- Dump-Groesse und erster `pg_restore`-Lauf sind gemessen: `immich.dump` 66M, echter Smoke-Test erfolgreich am 2026-05-27. - Dump-Groesse und erster `pg_restore`-Lauf sind gemessen: `immich.dump` 66M, echter Smoke-Test erfolgreich am 2026-05-27.
- pgvecto-rs Extension-Mismatch bei Image-Drift moeglich; Compose pinnt denselben Digest wie Produktion. - VectorChord-/pgvector-Extension-Mismatch bei Image-Drift moeglich; Compose pinnt denselben Digest wie Produktion.
- Immich v2 meldet pgvecto-rs als deprecated; VectorChord-Migration ist ein kuenftiger Immich-Upgrade-Punkt, nicht Teil dieses Restore-Tests. - Immich nutzt nach der VectorChord-Migration `vchord 0.4.3` und `vector 0.8.1`; Restore-Tests muessen ein Image mit VectorChord verwenden.
- Immich-Server-Migrations koennen Startup nach Restore verzoegern; Skript pollt 120 s. - Immich-Server-Migrations koennen Startup nach Restore verzoegern; Skript pollt 120 s.
- Bei Schema-Drift (z. B. nach Major-Update) koennen einzelne DB-Queries abweichen; das Skript versucht Immich-v2-Singular-Tabellen und aeltere Plural-Fallbacks. - Bei Schema-Drift (z. B. nach Major-Update) koennen einzelne DB-Queries abweichen; das Skript versucht Immich-v2-Singular-Tabellen und aeltere Plural-Fallbacks.
+3 -5
View File
@@ -87,7 +87,7 @@ Script: bash /mnt/user/services/homelab-infra/ops/renovate/run-renovate.sh
|---|---|---| |---|---|---|
| Major-Updates | `groupName: major-updates`, `automerge: false` | Eine gesammelte PR pro Lauf mit allen Major-Updates, manueller Merge | | Major-Updates | `groupName: major-updates`, `automerge: false` | Eine gesammelte PR pro Lauf mit allen Major-Updates, manueller Merge |
| Minor + Patch + Digest fuer Docker-Compose | `groupName: minor-and-patch-updates`, `automerge: false` | Eine gesammelte PR; Operator merged manuell | | Minor + Patch + Digest fuer Docker-Compose | `groupName: minor-and-patch-updates`, `automerge: false` | Eine gesammelte PR; Operator merged manuell |
| Tier-1-Datenhalter (Postgres, Mongo, Redis, pgvecto-rs) | `groupName: null`, eigener Label | Einzelne PRs ohne Group, hoehere Sichtbarkeit | | Tier-1-Datenhalter (Postgres, Mongo, Redis, Immich-Postgres) | `groupName: null`, eigener Label | Einzelne PRs ohne Group, hoehere Sichtbarkeit |
| Komodo-Major-Updates | `enabled: false` fuer matchPackageNames + matchUpdateTypes major | Komodo bleibt auf `:2`, wird nicht versehentlich auf `:3` migriert | | Komodo-Major-Updates | `enabled: false` fuer matchPackageNames + matchUpdateTypes major | Komodo bleibt auf `:2`, wird nicht versehentlich auf `:3` migriert |
| Lock-File-Maintenance | `lockFileMaintenance.enabled: false` | Renovate macht keine reinen Lock-File-Refreshs | | Lock-File-Maintenance | `lockFileMaintenance.enabled: false` | Renovate macht keine reinen Lock-File-Refreshs |
| Schedule | `extends ["schedule:weekly"]` | Renovate-Engine prueft, aber PRs/Updates folgen Wochen-Profilen wo sinnvoll | | Schedule | `extends ["schedule:weekly"]` | Renovate-Engine prueft, aber PRs/Updates folgen Wochen-Profilen wo sinnvoll |
@@ -105,12 +105,10 @@ Erstlauf 2026-05-29 erfolgreich: Renovate erzeugte das Dependency Dashboard und
- `renovate/mongo-7.x` - `renovate/mongo-7.x`
- `renovate/postgres-17.x` - `renovate/postgres-17.x`
Kontrolllauf 2026-05-31 13:24 MESZ: `rc=0`, Dashboard aktualisiert, keine Minor-/Patch-Nachzuegler offen. Offen bleiben nur Major-PRs: Stand nach den 2026-05-31-Migrationen: PostgreSQL 18, Redis 8 und Immich-Postgres mit VectorChord sind produktiv ausgerollt und in `renovate.json` per `allowedVersions` auf die jeweiligen Major-/Image-Schienen begrenzt. Offene Renovate-PRs fuer genau diese Migrationen werden geschlossen statt gemerged, falls sie im Gitea-Dashboard noch vorhanden sind. Offen bleiben als bewusst separate Entscheidungen:
- #7 `renovate/major-major-updates`: Grafana 13 - #7 `renovate/major-major-updates`: Grafana 13
- #8 `renovate/mongo-8.x`: Mongo 8 fuer Komodo-Mongo - #8 `renovate/mongo-8.x`: Mongo 8 fuer Komodo-Mongo
- #9 `renovate/postgres-18.x`: Postgres 18 fuer `postgresql17`, Mealie und Nextcloud
- #10 `renovate/redis-8.x`: Redis 8 fuer Immich, Nextcloud und shared Redis
Diese Major-PRs sind reine Entscheidungsvorlagen und werden nicht automatisch gemerged. Die Komodo-Core-/Periphery-Major-Sperre wurde verifiziert: `komodo-core` und `komodo-periphery` tauchen in keinem Major-PR-Diff auf. Diese Major-PRs sind reine Entscheidungsvorlagen und werden nicht automatisch gemerged. Die Komodo-Core-/Periphery-Major-Sperre wurde verifiziert: `komodo-core` und `komodo-periphery` tauchen in keinem Major-PR-Diff auf.
@@ -118,7 +116,7 @@ Diese Major-PRs sind reine Entscheidungsvorlagen und werden nicht automatisch ge
Beim Erstlauf wird Renovate vermutlich PRs fuer einige der digest-gepinnten Images oeffnen, weil diese Digests seit Wochen nicht erneuert wurden. Reihenfolge der Sichtpruefung: Beim Erstlauf wird Renovate vermutlich PRs fuer einige der digest-gepinnten Images oeffnen, weil diese Digests seit Wochen nicht erneuert wurden. Reihenfolge der Sichtpruefung:
1. **Stateful Tier-1 zuerst, einzeln**: Postgres, Redis, Mongo, pgvecto-rs - jeder eigener PR, einzeln pruefen und mergen. Smoke-Test nach Merge ueber Komodo-Webhook-Deploy beobachten. 1. **Stateful Tier-1 zuerst, einzeln**: Postgres, Redis, Mongo, Immich-Postgres - jeder eigener PR, einzeln pruefen und mergen. Smoke-Test nach Merge ueber Komodo-Webhook-Deploy beobachten.
2. **Gruppe minor-and-patch-updates**: Alle anderen Docker-Compose-Images zusammen. Wenn der Diff vernuenftig aussieht, mergen. 2. **Gruppe minor-and-patch-updates**: Alle anderen Docker-Compose-Images zusammen. Wenn der Diff vernuenftig aussieht, mergen.
3. **Gruppe major-updates**: Erst nach Operator-Sichtpruefung pro Image, ggf. zurueckstellen oder manuell entscheiden. 3. **Gruppe major-updates**: Erst nach Operator-Sichtpruefung pro Image, ggf. zurueckstellen oder manuell entscheiden.
+11 -11
View File
@@ -1,6 +1,6 @@
# Repository Map # Repository Map
Stand: 2026-05-23 Stand: 2026-05-31
Diese Datei ist eine technische Landkarte des Repositories. Sie wurde aus Markdown-Dokumenten, `docker-compose.yml`-Dateien, Env-Beispielen, Traefik-Dynamic-Configs, Komodo/Periphery-Dateien und Skripten abgeleitet. Sie beschreibt den Repo-Sollzustand, nicht zwingend den Live-Zustand auf dem Host. Diese Datei ist eine technische Landkarte des Repositories. Sie wurde aus Markdown-Dokumenten, `docker-compose.yml`-Dateien, Env-Beispielen, Traefik-Dynamic-Configs, Komodo/Periphery-Dateien und Skripten abgeleitet. Sie beschreibt den Repo-Sollzustand, nicht zwingend den Live-Zustand auf dem Host.
@@ -102,11 +102,11 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| Stack | Compose | Services / Images | Traefik Hosts | Networks | Ports | Abhaengigkeiten | | Stack | Compose | Services / Images | Traefik Hosts | Networks | Ports | Abhaengigkeiten |
|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|
| Gitea | `core/gitea/docker-compose.yml` | `gitea` -> `docker.gitea.com/gitea:1.25.4@sha256:...` | `git.kaleschke.info` | `frontend_net` | `222:22/tcp` | SQLite in `/data`; SSH-Port ist dokumentierte Ausnahme; `github.com` ist als Mirror-Ziel erlaubt und externe DNS-Resolver sind gesetzt | | Gitea | `core/gitea/docker-compose.yml` | `gitea` -> `docker.gitea.com/gitea:1.25.4@sha256:...` | `git.kaleschke.info` | `frontend_net` | `222:22/tcp` | SQLite in `/data`; SSH-Port ist dokumentierte Ausnahme; `github.com` ist als Mirror-Ziel erlaubt und externe DNS-Resolver sind gesetzt |
| Authelia | `security/authelia/docker-compose.yml` | `authelia` -> `authelia/authelia:latest@sha256:...` | `auth.kaleschke.info` | `frontend_net`, `backend_net` | keine | PostgreSQL 17 Storage, Traefik ForwardAuth; bewusst ohne Redis-Session-Backend | | Authelia | `security/authelia/docker-compose.yml` | `authelia` -> `authelia/authelia:latest@sha256:...` | `auth.kaleschke.info` | `frontend_net`, `backend_net` | keine | PostgreSQL 18 Storage im historisch benannten `postgresql17`-Container, Traefik ForwardAuth; bewusst ohne Redis-Session-Backend |
| Vaultwarden | `security/vaultwarden/docker-compose.yml` | `vaultwarden` -> `vaultwarden/server:latest@sha256:...` | `vault.kaleschke.info` | `frontend_net` | keine | Datei-Persistenz, `ADMIN_TOKEN_FILE` | | Vaultwarden | `security/vaultwarden/docker-compose.yml` | `vaultwarden` -> `vaultwarden/server:latest@sha256:...` | `vault.kaleschke.info` | `frontend_net` | keine | Datei-Persistenz, `ADMIN_TOKEN_FILE` |
| ddns-updater | `infra/ddns-updater/docker-compose.yml` | `ddns-updater` -> `ghcr.io/qdm12/ddns-updater:latest@sha256:...` | keine | `frontend_net` | keine | Cloudflare/API-Internetbedarf | | ddns-updater | `infra/ddns-updater/docker-compose.yml` | `ddns-updater` -> `ghcr.io/qdm12/ddns-updater:latest@sha256:...` | keine | `frontend_net` | keine | Cloudflare/API-Internetbedarf |
| PostgreSQL 17 | `infra/postgresql17/docker-compose.yml` | `postgresql17` -> `postgres:17.9@sha256:...` | keine | `backend_net` | keine | shared DB-Cluster | | PostgreSQL 18 | `infra/postgresql17/docker-compose.yml` | `postgresql17` -> `postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39` | keine | `backend_net` | keine | shared DB-Cluster; Service-Name bleibt historisch `postgresql17` |
| Redis | `infra/redis/docker-compose.yml` | `Redis` -> `redis:7.4-alpine@sha256:...` | keine | `backend_net` | keine | primaer Paperless-Redis (App-Cache); historisch als "shared" angelegt, faktisch nur von Paperless genutzt; Passwort-Datei | | Redis | `infra/redis/docker-compose.yml` | `Redis` -> `redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1` | keine | `backend_net` | keine | primaer Paperless-Redis (App-Cache); historisch als "shared" angelegt, faktisch nur von Paperless genutzt; Passwort-Datei |
### Host Services ### Host Services
@@ -169,7 +169,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| Network | Typ / Status | Nutzer | | Network | Typ / Status | Nutzer |
|---|---|---| |---|---|---|
| `frontend_net` | external bridge | Web-/Proxy-Netz fuer Traefik und alle gerouteten UIs | | `frontend_net` | external bridge | Web-/Proxy-Netz fuer Traefik und alle gerouteten UIs |
| `backend_net` | external/internal laut Architektur | PostgreSQL 17, Redis, Authelia, Paperless, Mail Archiver, Traefik | | `backend_net` | external/internal laut Architektur | PostgreSQL 18 (`postgresql17`), Redis 8, Authelia, Paperless, Mail Archiver, Traefik |
| `dns_net` | App-/Host-Netz | AdGuard Home und Unbound | | `dns_net` | App-/Host-Netz | AdGuard Home und Unbound |
| `immich_default` | Compose-intern, `internal: true` | Immich Server, ML, Postgres, Redis | | `immich_default` | Compose-intern, `internal: true` | Immich Server, ML, Postgres, Redis |
| `mealie_internal` | Compose-intern; Laufzeitname mit Compose-Projektpraefix typischerweise `mealie_mealie_internal` | Mealie und Mealie Postgres | | `mealie_internal` | Compose-intern; Laufzeitname mit Compose-Projektpraefix typischerweise `mealie_mealie_internal` | Mealie und Mealie Postgres |
@@ -189,13 +189,13 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| Gitea | `/mnt/user/services/gitea/data` | | Gitea | `/mnt/user/services/gitea/data` |
| Authelia | `/mnt/user/appdata/authelia/config`, Authelia Secret-Dateien | | Authelia | `/mnt/user/appdata/authelia/config`, Authelia Secret-Dateien |
| Vaultwarden | `/mnt/user/appdata/vaultwarden`, Admin-Token-Datei | | Vaultwarden | `/mnt/user/appdata/vaultwarden`, Admin-Token-Datei |
| PostgreSQL 17 | `/mnt/user/appdata/postgresql17`, `postgres_password.txt` | | PostgreSQL 18 | `/mnt/user/appdata/postgresql18`, Rollback-Altstand `/mnt/user/appdata/postgresql17`, `postgres_password.txt` |
| Redis | `/mnt/user/appdata/redis`, `redis_password.txt` | | Redis | `/mnt/user/appdata/redis`, `redis_password.txt` |
| Paperless | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/scans_inbox` | | Paperless | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/scans_inbox` |
| Immich | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive`, `/mnt/user/appdata/immich_postgres`, `model-cache` | | Immich | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive`, `/mnt/user/appdata/immich_postgres_vectorchord`, Rollback-Altstand `/mnt/user/appdata/immich_postgres`, `model-cache` |
| Mealie | `/mnt/user/appdata/mealie/data`, `/mnt/user/appdata/mealie/postgres` | | Mealie | `/mnt/user/appdata/mealie/data`, `/mnt/user/appdata/mealie/postgres18`, Rollback-Altstand `/mnt/user/appdata/mealie/postgres` |
| Mail Archiver | `/mnt/user/appdata/mailarchiver/data-protection-keys` | | Mail Archiver | `/mnt/user/appdata/mailarchiver/data-protection-keys` |
| Nextcloud | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data`, `/mnt/user/appdata/nextcloud/postgres`, `/mnt/user/appdata/nextcloud/redis` | | Nextcloud | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data`, `/mnt/user/appdata/nextcloud/postgres18`, Rollback-Altstand `/mnt/user/appdata/nextcloud/postgres`, `/mnt/user/appdata/nextcloud/redis` |
| Plex | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` | | Plex | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` |
| ntfy | `/mnt/user/appdata/ntfy` | | ntfy | `/mnt/user/appdata/ntfy` |
| Paperless-GPT | `/mnt/user/appdata/paperless-gpt/data`, `/mnt/user/appdata/paperless-gpt/prompts` | | Paperless-GPT | `/mnt/user/appdata/paperless-gpt/data`, `/mnt/user/appdata/paperless-gpt/prompts` |
@@ -219,7 +219,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
| Traefik | `cloudflare_dns_api_token` als Docker Secret / `CF_DNS_API_TOKEN_FILE` | | Traefik | `cloudflare_dns_api_token` als Docker Secret / `CF_DNS_API_TOKEN_FILE` |
| Authelia | JWT, Session, Storage Encryption, Postgres Password via `_FILE` | | Authelia | JWT, Session, Storage Encryption, Postgres Password via `_FILE` |
| Vaultwarden | `ADMIN_TOKEN_FILE` | | Vaultwarden | `ADMIN_TOKEN_FILE` |
| PostgreSQL 17 | `POSTGRES_PASSWORD_FILE` | | PostgreSQL 18 | `POSTGRES_PASSWORD_FILE` |
| Redis | Passwort-Datei und Startkommando | | Redis | Passwort-Datei und Startkommando |
| Paperless | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS` als Komodo Stack ENV | | Paperless | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS` als Komodo Stack ENV |
| Immich | `IMMICH_DB_PASSWORD` Stack ENV; `immich_postgres_password.txt` fuer Postgres | | Immich | `IMMICH_DB_PASSWORD` Stack ENV; `immich_postgres_password.txt` fuer Postgres |
@@ -251,7 +251,7 @@ Das Skript liest Secret-Dateien auf dem Host und schreibt Dump-Artefakte. Bei An
- Authelia `configuration.yml` ist Repo-Baseline fuer nicht geheime Einstellungen, wird aber nicht automatisch von Komodo auf den Host kopiert. Die produktive Host-Datei kann zusaetzliche OIDC-/Secret-Konfiguration enthalten; Aenderungen muessen manuell gemerged und validiert werden. - Authelia `configuration.yml` ist Repo-Baseline fuer nicht geheime Einstellungen, wird aber nicht automatisch von Komodo auf den Host kopiert. Die produktive Host-Datei kann zusaetzliche OIDC-/Secret-Konfiguration enthalten; Aenderungen muessen manuell gemerged und validiert werden.
- `backend_net` ist in der Architektur als `internal: true` beschrieben; einzelne Compose-Dateien referenzieren es external. Live-Netz-Attribute bei Drift-Fragen pruefen. - `backend_net` ist in der Architektur als `internal: true` beschrieben; einzelne Compose-Dateien referenzieren es external. Live-Netz-Attribute bei Drift-Fragen pruefen.
- Einige Images bleiben trotz Digest-Pin semantisch auf mutable Tags (`latest@sha256`, `release@sha256`). Das ist bewusst dokumentiert, aber bei Updates gesondert pruefen. - Einige Images bleiben trotz Digest-Pin semantisch auf mutable Tags (`latest@sha256`, `release@sha256`). Das ist bewusst dokumentiert, aber bei Updates gesondert pruefen.
- Stateful Datenhalter sind seit 2026-05-05 bevorzugt mit Minor-/Patch-Tag plus Digest gepinnt; Redis-Caches wurden im Hardening-Sprint 2026-05-16 auf `redis:7.4-alpine@sha256:...` vereinheitlicht. - Stateful Datenhalter sind seit 2026-05-05 bevorzugt mit Minor-/Patch-Tag plus Digest gepinnt; PostgreSQL-17-Datenhalter wurden am 2026-05-31 per Dump/Restore auf PostgreSQL 18 gehoben, Redis-Caches auf Redis 8.8, Immich-Postgres bleibt auf PG14 mit VectorChord.
- `scrutiny` bleibt `privileged: true`; dokumentierte Ausnahme, aber weiterhin pruefenswert. - `scrutiny` bleibt `privileged: true`; dokumentierte Ausnahme, aber weiterhin pruefenswert.
- `tailscale` nutzt Host-Netz, `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-Ausnahme. - `tailscale` nutzt Host-Netz, `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-Ausnahme.
- `monitoring-influxdb3-core` laeuft aktuell als `user: "0"`; UID/GID-Hardening nur als eigener Sprint. - `monitoring-influxdb3-core` laeuft aktuell als `user: "0"`; UID/GID-Hardening nur als eigener Sprint.
+16 -8
View File
@@ -30,9 +30,9 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
| Traefik | Share / Borg | `/mnt/user/appdata/traefik`, besonders `dynamic/`, `letsencrypt`, `secrets` | keine eigene DB | `cloudflare_dns_api_token` | `frontend_net`, `backend_net` | `https://traefik.kaleschke.info` erreichbar, Dashboard ueber Authelia | | Traefik | Share / Borg | `/mnt/user/appdata/traefik`, besonders `dynamic/`, `letsencrypt`, `secrets` | keine eigene DB | `cloudflare_dns_api_token` | `frontend_net`, `backend_net` | `https://traefik.kaleschke.info` erreichbar, Dashboard ueber Authelia |
| AdGuard Home | Share / Borg | `/mnt/user/appdata/adguard/conf` | keine | keine zusaetzlichen Repo-Secrets dokumentiert | `dns_net`, `frontend_net` | DNS-Aufloesung funktioniert | | AdGuard Home | Share / Borg | `/mnt/user/appdata/adguard/conf` | keine | keine zusaetzlichen Repo-Secrets dokumentiert | `dns_net`, `frontend_net` | DNS-Aufloesung funktioniert |
| Tailscale | Share / Borg | `/mnt/user/appdata/tailscale` | keine | Tailscale-State im Pfad | Host-Netz | Tailscale verbunden | | Tailscale | Share / Borg | `/mnt/user/appdata/tailscale` | keine | Tailscale-State im Pfad | Host-Netz | Tailscale verbunden |
| PostgreSQL 17 | Share + Dumps | `/mnt/user/appdata/postgresql17` | `postgresql17-globals.sql`, `postgresql17-mailarchiver.dump`, `postgresql17-paperless.dump`, optional `postgresql17-authelia.dump` | `postgres_password.txt` | `backend_net` | DB startet, Ziel-Datenbanken vorhanden | | PostgreSQL 18 | Share + Dumps | `/mnt/user/appdata/postgresql18` (Rollback-Altstand: `/mnt/user/appdata/postgresql17`) | `postgresql17-globals.sql`, `postgresql17-mailarchiver.dump`, `postgresql17-paperless.dump`, optional `postgresql17-authelia.dump` | `postgres_password.txt`, App-Rollen-Passwoerter aus den jeweiligen Stack-ENV/Secret-Dateien | `backend_net` | DB startet, Ziel-Datenbanken vorhanden; `SHOW data_checksums` ist `on` |
| Redis | Share / Host | `/mnt/user/appdata/redis` | keine | `redis_password.txt` | `backend_net` | Redis startet, Apps verbinden sich | | Redis 8 | Share / Host | `/mnt/user/appdata/redis`; Rollback-Backup unter `/mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-<ts>` | RDB/AOF-Dateien im Datenpfad | `redis_password.txt` | `backend_net` | Redis startet, `redis_version` ist 8.x, Apps verbinden sich |
| Authelia | Borg | `/mnt/user/appdata/authelia/config`, `/mnt/user/appdata/secrets/*authelia*` | Shared PostgreSQL, optional Dump `postgresql17-authelia.dump` | JWT/Session/Storage/Postgres-/SMTP-Secret-Dateien | PostgreSQL 17, Traefik, GMX SMTP | Login-Seite und ForwardAuth funktionieren; SMTP-Notifier startet; aktive Sessions werden nach Restart neu aufgebaut | | Authelia | Borg | `/mnt/user/appdata/authelia/config`, `/mnt/user/appdata/secrets/*authelia*` | Shared PostgreSQL 18, optional Dump `postgresql17-authelia.dump` | JWT/Session/Storage/Postgres-/SMTP-Secret-Dateien | PostgreSQL 18, Traefik, GMX SMTP | Login-Seite und ForwardAuth funktionieren; SMTP-Notifier startet; aktive Sessions werden nach Restart neu aufgebaut |
| Gitea | GitHub-Mirror + Gitea-Bundles fuer Repo-Bootstrap, Borg + Dump fuer Gitea-Appstate | `/mnt/user/services/gitea/data`, `/mnt/user/backups/git-bundles/gitea` | `gitea.sqlite.dump`, Bundle-Report `latest-report.md` | `borg_repo_passphrase.txt` fuer Restore-Tests; GitHub-Push-Mirror-PAT liegt nur in Gitea-Mirror-Settings | Traefik | Web-UI erreichbar, Repo sichtbar, SSH-Port reagiert; Bundle laesst sich klonen und `git fsck` ist sauber; GitHub-Push-Mirror synchronisiert ohne `last_error`; Mini-Restore nach `/mnt/user/backups/restore-lab/gitea` am 2026-05-07 erfolgreich validiert | | Gitea | GitHub-Mirror + Gitea-Bundles fuer Repo-Bootstrap, Borg + Dump fuer Gitea-Appstate | `/mnt/user/services/gitea/data`, `/mnt/user/backups/git-bundles/gitea` | `gitea.sqlite.dump`, Bundle-Report `latest-report.md` | `borg_repo_passphrase.txt` fuer Restore-Tests; GitHub-Push-Mirror-PAT liegt nur in Gitea-Mirror-Settings | Traefik | Web-UI erreichbar, Repo sichtbar, SSH-Port reagiert; Bundle laesst sich klonen und `git fsck` ist sauber; GitHub-Push-Mirror synchronisiert ohne `last_error`; Mini-Restore nach `/mnt/user/backups/restore-lab/gitea` am 2026-05-07 erfolgreich validiert |
| Komodo | Borg / Share | `/mnt/user/appdata/komodo/core`, `/mnt/user/appdata/komodo/periphery`, `/mnt/user/services/stacks` | `komodo-mongo.archive.gz` falls verifiziert | `komodo_mongo_password.txt`, `KOMODO_*` Stack ENV | Traefik, Mongo, Gitea | UI erreichbar, Periphery verbunden | | Komodo | Borg / Share | `/mnt/user/appdata/komodo/core`, `/mnt/user/appdata/komodo/periphery`, `/mnt/user/services/stacks` | `komodo-mongo.archive.gz` falls verifiziert | `komodo_mongo_password.txt`, `KOMODO_*` Stack ENV | Traefik, Mongo, Gitea | UI erreichbar, Periphery verbunden |
| GitOps Host Automation | Borg / Git | `/mnt/user/services/homelab-infra`, `/mnt/user/services/posture-check` | keine eigene DB | keine | Gitea, Komodo, Unraid User Scripts | `posture-check` laeuft vom Host-Pfad und liefert `warning_count: 0` im bekannten Uebergangszustand | | GitOps Host Automation | Borg / Git | `/mnt/user/services/homelab-infra`, `/mnt/user/services/posture-check` | keine eigene DB | keine | Gitea, Komodo, Unraid User Scripts | `posture-check` laeuft vom Host-Pfad und liefert `warning_count: 0` im bekannten Uebergangszustand |
@@ -44,11 +44,11 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
| Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test | | Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test |
|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|
| Paperless-ngx | Borg + Dumps | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/paperless/export`, `/mnt/user/documents/scans_inbox` | `postgresql17-paperless.dump` | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS`, `borg_repo_passphrase.txt` fuer Restore-Tests | PostgreSQL 17, Redis, Traefik | Web-UI startet, Dokumente vorhanden; Mini-Restore nach `/mnt/user/backups/restore-lab/paperless` am 2026-05-07 erfolgreich validiert | | Paperless-ngx | Borg + Dumps | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/paperless/export`, `/mnt/user/documents/scans_inbox` | `postgresql17-paperless.dump` | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS`, `borg_repo_passphrase.txt` fuer Restore-Tests | PostgreSQL 18, Redis, Traefik | Web-UI startet, Dokumente vorhanden; Mini-Restore nach `/mnt/user/backups/restore-lab/paperless` am 2026-05-07 erfolgreich validiert |
| Mealie | Borg + Dump | `/mnt/user/appdata/mealie/data`, optional `/mnt/user/appdata/mealie/postgres` bei lokalem Share-Weiterbetrieb | `mealie.dump` | `mealie_postgres_password.txt` | `mealie-postgres`, Traefik | UI startet, Rezepte vorhanden | | Mealie | Borg + Dump | `/mnt/user/appdata/mealie/data`, `/mnt/user/appdata/mealie/postgres18` (Rollback-Altstand: `/mnt/user/appdata/mealie/postgres`) | `mealie.dump` | `mealie_postgres_password.txt` | `mealie-postgres`, Traefik | UI startet, Rezepte vorhanden |
| Immich | Borg + Dump | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` | `immich.dump` | `IMMICH_DB_PASSWORD`, `immich_postgres_password.txt`, `borg_repo_passphrase.txt` fuer Restore-Tests | `immich_postgres`, `immich_redis`, Traefik | DB- und UI-Smoke gegen produktives Borg-Archiv am 2026-05-27 erfolgreich validiert: `immich.dump` extrahiert, isolierter pgvecto-rs-Postgres importiert, Immich-Loginseite HTTP 200, `11977` Assets und `1` User im Test-DB-Check; Report `/mnt/user/backups/restore-reports/immich-2026-05-27.md`. Voll-Restore der Foto-Dateien bleibt separater DR-Drill | | Immich | Borg + Dump | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive`, `/mnt/user/appdata/immich_postgres_vectorchord`; Rollback-Altstand: `/mnt/user/appdata/immich_postgres` | `immich.dump`; nach VectorChord braucht ein Restore ein Postgres-Image mit VectorChord | `IMMICH_DB_PASSWORD`, `immich_postgres_password.txt`, `borg_repo_passphrase.txt` fuer Restore-Tests | `immich_postgres`, `immich_redis`, Traefik | DB- und UI-Smoke gegen produktives Borg-Archiv am 2026-05-27 erfolgreich validiert; VectorChord-Migration am 2026-05-31: `11977` Assets, `11107` Smart-Search-Zeilen, `7092` Face-Search-Zeilen, `vchord 0.4.3`, `vector 0.8.1`, HTTP/API-Smoke 200. Voll-Restore der Foto-Dateien bleibt separater DR-Drill |
| Mail-Archiver | Borg + Shared Dump | `/mnt/user/appdata/mailarchiver/data-protection-keys` | `postgresql17-mailarchiver.dump` | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` | PostgreSQL 17, Traefik, Authelia | Authelia-Weiterleitung greift; nach Login startet die Web-UI und das Archiv laesst sich oeffnen | | Mail-Archiver | Borg + Shared Dump | `/mnt/user/appdata/mailarchiver/data-protection-keys` | `postgresql17-mailarchiver.dump` | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` | PostgreSQL 18, Traefik, Authelia | Authelia-Weiterleitung greift; nach Login startet die Web-UI und das Archiv laesst sich oeffnen |
| Nextcloud | Borg + Dump | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | `nextcloud.dump` | `nextcloud_admin_user.txt`, `nextcloud_admin_password.txt`, `nextcloud_postgres_password.txt` | `nextcloud-postgres`, `nextcloud-redis`, Traefik | Web-UI startet, Login funktioniert, Dateien sichtbar | | Nextcloud | Borg + Dump | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data`, `/mnt/user/appdata/nextcloud/postgres18` (Rollback-Altstand: `/mnt/user/appdata/nextcloud/postgres`), `/mnt/user/appdata/nextcloud/redis` | `nextcloud.dump`; Redis-Backup vor Redis-8-Cutover unter `/mnt/user/backups/borg/dumps/latest/nextcloud-redis-pre-redis8-<ts>` | `nextcloud_admin_user.txt`, `nextcloud_admin_password.txt`, `nextcloud_postgres_password.txt`; produktive DB-Rolle laut `config.php` ist `oc_admin` | `nextcloud-postgres`, `nextcloud-redis`, Traefik | Web-UI startet, Login funktioniert, Dateien sichtbar; `occ status` zeigt `maintenance: false` |
| Glance | Git / Borg-Repo | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine kritische Datenpersistenz | keine | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` | Traefik, Authelia, optional interne API-Ziele | Dashboard startet, Widgets laden, Docker-Status laeuft nur ueber `glance-docker-socket-proxy` | | Glance | Git / Borg-Repo | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine kritische Datenpersistenz | keine | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` | Traefik, Authelia, optional interne API-Ziele | Dashboard startet, Widgets laden, Docker-Status laeuft nur ueber `glance-docker-socket-proxy` |
| ntfy | Borg / Share | `/mnt/user/appdata/ntfy` | keine | keine besonderen Secret-Dateien dokumentiert | Traefik | UI und Push-Endpunkt erreichbar | | ntfy | Borg / Share | `/mnt/user/appdata/ntfy` | keine | keine besonderen Secret-Dateien dokumentiert | Traefik | UI und Push-Endpunkt erreichbar |
| Paperless-GPT | Borg / Share | `/mnt/user/appdata/paperless-gpt` | keine eigene DB | `PAPERLESS_API_TOKEN` | Traefik, Paperless | UI startet, Konfiguration vorhanden | | Paperless-GPT | Borg / Share | `/mnt/user/appdata/paperless-gpt` | keine eigene DB | `PAPERLESS_API_TOKEN` | Traefik, Paperless | UI startet, Konfiguration vorhanden |
@@ -97,6 +97,14 @@ Aktuell relevante Dump-Artefakte unter `/mnt/user/backups/borg/dumps/latest`:
Die Dump-Erzeugung ist host-seitig ueber `ops/borg-ui/scripts/pre-backup-dumps.sh` vorgesehen. Die Dump-Erzeugung ist host-seitig ueber `ops/borg-ui/scripts/pre-backup-dumps.sh` vorgesehen.
### PostgreSQL 18 Restore- und Rollback-Regeln
- PostgreSQL-18-Container verwenden das Docker-Image-Layout mit Mount auf `/var/lib/postgresql` und `PGDATA=/var/lib/postgresql/18/docker`.
- Die alten PostgreSQL-17-Datenpfade bleiben nach dem Major-Upgrade als Rollback-Altstand erhalten und duerfen erst nach separater Freigabe geloescht werden.
- Shared-Cluster-Restore: zuerst `pg_dumpall --globals-only` einspielen, dann die einzelnen Custom-Format-Dumps per `pg_restore`. Der Bootstrap-Rollenkonflikt fuer `mailarchiver` ist benign, solange `CREATE ROLE mailarchiver;` gezielt ausgelassen und das folgende `ALTER ROLE mailarchiver ...` eingespielt wird.
- Nextcloud-Restore: vor dem Dump `occ maintenance:mode --on`, nach erfolgreichem Restore und `occ status` wieder `occ maintenance:mode --off`. Die Rolle `oc_admin` muss mit dem in `config.php` hinterlegten DB-Passwort existieren.
- Rollback: betroffene App(s) und DB stoppen, Compose auf das vorherige PostgreSQL-17-Image und den alten Datenpfad zuruecksetzen, dann DB und App wieder starten.
--- ---
## Praktische Restore-Regeln ## Praktische Restore-Regeln
+1 -1
View File
@@ -18,7 +18,7 @@ Dieses Dokument listet sensible Daten, deren Ablageorte und die vorgesehene Einb
|---|---|---|---| |---|---|---|---|
| Vaultwarden | `ADMIN_TOKEN` | `/mnt/user/appdata/secrets/vaultwarden_admin_token.txt` -> `ADMIN_TOKEN_FILE` | aktiv | | Vaultwarden | `ADMIN_TOKEN` | `/mnt/user/appdata/secrets/vaultwarden_admin_token.txt` -> `ADMIN_TOKEN_FILE` | aktiv |
| Traefik | Cloudflare DNS API Token | `/mnt/user/appdata/traefik/secrets/cloudflare_dns_api_token` -> Docker Secret `cloudflare_dns_api_token` | aktiv | | Traefik | Cloudflare DNS API Token | `/mnt/user/appdata/traefik/secrets/cloudflare_dns_api_token` -> Docker Secret `cloudflare_dns_api_token` | aktiv |
| PostgreSQL 17 | DB Password | `/mnt/user/appdata/secrets/postgres_password.txt` -> `POSTGRES_PASSWORD_FILE` | aktiv | | PostgreSQL 18 | DB Password | `/mnt/user/appdata/secrets/postgres_password.txt` -> `POSTGRES_PASSWORD_FILE` | aktiv |
| Redis | Passwort | `/mnt/user/appdata/secrets/redis_password.txt` -> Datei-Mount + Startkommando in `infra/redis/docker-compose.yml` | aktiv | | Redis | Passwort | `/mnt/user/appdata/secrets/redis_password.txt` -> Datei-Mount + Startkommando in `infra/redis/docker-compose.yml` | aktiv |
| Mealie | DB Password | `/mnt/user/appdata/secrets/mealie_postgres_password.txt` -> nicht versionierte Stack-`.env` `${MEALIE_POSTGRES_PASSWORD}` -> `POSTGRES_PASSWORD` | aktiv | | Mealie | DB Password | `/mnt/user/appdata/secrets/mealie_postgres_password.txt` -> nicht versionierte Stack-`.env` `${MEALIE_POSTGRES_PASSWORD}` -> `POSTGRES_PASSWORD` | aktiv |
| mealie-postgres | DB Password | `/mnt/user/appdata/secrets/mealie_postgres_password.txt` -> `POSTGRES_PASSWORD_FILE` | aktiv | | mealie-postgres | DB Password | `/mnt/user/appdata/secrets/mealie_postgres_password.txt` -> `POSTGRES_PASSWORD_FILE` | aktiv |
+11 -11
View File
@@ -1,6 +1,6 @@
# Service Catalog # Service Catalog
Stand: 2026-05-23 Stand: 2026-05-31
Dieser Katalog beschreibt produktive und repo-vorbereitete Dienste aus Sicht von Betrieb, Restore und KI-Kontext. Er basiert auf dem Repo-Sollzustand. Vor produktiven Eingriffen immer den Live-Zustand in Komodo/Docker pruefen. Dieser Katalog beschreibt produktive und repo-vorbereitete Dienste aus Sicht von Betrieb, Restore und KI-Kontext. Er basiert auf dem Repo-Sollzustand. Vor produktiven Eingriffen immer den Live-Zustand in Komodo/Docker pruefen.
@@ -20,33 +20,33 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und
| Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs | | Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs |
|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|
| `authelia` | ForwardAuth / zentrale Auth fuer Admin-UIs | `security/authelia/docker-compose.yml`, `security/authelia/configuration.yml` | `https://auth.kaleschke.info` | PostgreSQL 17, Traefik, GMX SMTP | `/mnt/user/appdata/authelia/config`, Authelia Secret-Dateien | Tier 1, config + DB + secrets | ja | Bewusst ohne Redis-Session-Backend; SMTP-Notifier via GMX und `authelia_smtp_password.txt`; explizite DNS-Server fuer SMTP/NTP; Repo-Baseline muss manuell in die Host-Config gemerged werden, OIDC/Secrets bleiben hostseitig; Access-Control und Compose-Middleware bei Aenderungen abgleichen | | `authelia` | ForwardAuth / zentrale Auth fuer Admin-UIs | `security/authelia/docker-compose.yml`, `security/authelia/configuration.yml` | `https://auth.kaleschke.info` | PostgreSQL 18, Traefik, GMX SMTP | `/mnt/user/appdata/authelia/config`, Authelia Secret-Dateien | Tier 1, config + DB + secrets | ja | Bewusst ohne Redis-Session-Backend; SMTP-Notifier via GMX und `authelia_smtp_password.txt`; explizite DNS-Server fuer SMTP/NTP; Repo-Baseline muss manuell in die Host-Config gemerged werden, OIDC/Secrets bleiben hostseitig; Access-Control und Compose-Middleware bei Aenderungen abgleichen |
| `vaultwarden` | Passwort-Tresor | `security/vaultwarden/docker-compose.yml` | `https://vault.kaleschke.info` | Traefik, `frontend_net` | `/mnt/user/appdata/vaultwarden` | Tier 1, `vaultwarden.sqlite.dump` + Share | ja | `ADMIN_TOKEN_FILE`; keine direkten Ports | | `vaultwarden` | Passwort-Tresor | `security/vaultwarden/docker-compose.yml` | `https://vault.kaleschke.info` | Traefik, `frontend_net` | `/mnt/user/appdata/vaultwarden` | Tier 1, `vaultwarden.sqlite.dump` + Share | ja | `ADMIN_TOKEN_FILE`; keine direkten Ports |
## Shared Infrastructure ## Shared Infrastructure
| Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs | | Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs |
|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|
| `postgresql17` | shared PostgreSQL Cluster | `infra/postgresql17/docker-compose.yml` | intern | `backend_net` | `/mnt/user/appdata/postgresql17`, `postgres_password.txt` | Tier 1; Dumps unter `/mnt/user/backups/borg/dumps/latest` | nein | keine Host-Ports; raw DB nicht primaerer Restore-Weg | | `postgresql17` | shared PostgreSQL 18 Cluster (historischer Service-Name bleibt fuer DNS/Clients stabil) | `infra/postgresql17/docker-compose.yml` | intern | `backend_net` | `/mnt/user/appdata/postgresql18`, Rollback-Altstand `/mnt/user/appdata/postgresql17`, `postgres_password.txt` | Tier 1; Dumps unter `/mnt/user/backups/borg/dumps/latest` | nein | keine Host-Ports; raw DB nicht primaerer Restore-Weg |
| `Redis` | primaer Paperless-Redis (App-Cache); historisch als "shared" angelegt, faktisch nur von Paperless genutzt | `infra/redis/docker-compose.yml` | intern | `backend_net` | `/mnt/user/appdata/redis`, `redis_password.txt` | transiente Daten, bewusst nicht kritisch | nein | Passwort-Datei; optional named volume offen. Immich, Nextcloud und Mealie nutzen jeweils eigene Redis-Instanzen; Authelia laeuft bewusst ohne Redis-Session-Backend. Bei Wegfall ist Paperless der einzige betroffene Stack. | | `Redis` | primaer Paperless-Redis (App-Cache); historisch als "shared" angelegt, faktisch nur von Paperless genutzt | `infra/redis/docker-compose.yml` | intern | `backend_net` | `/mnt/user/appdata/redis`, `redis_password.txt` | transiente Daten, bewusst nicht kritisch | nein | Redis 8.8; Passwort-Datei; optional named volume offen. Immich, Nextcloud und Mealie nutzen jeweils eigene Redis-Instanzen; Authelia laeuft bewusst ohne Redis-Session-Backend. Bei Wegfall ist Paperless der einzige betroffene Stack. |
| `ddns-updater` | Cloudflare/DDNS Aktualisierung | `infra/ddns-updater/docker-compose.yml` | intern | Internetzugang, `frontend_net` | `/mnt/user/appdata/ddns-updater` | rebuildbar | nein | bleibt bewusst in `frontend_net`, weil `backend_net` internal ist | | `ddns-updater` | Cloudflare/DDNS Aktualisierung | `infra/ddns-updater/docker-compose.yml` | intern | Internetzugang, `frontend_net` | `/mnt/user/appdata/ddns-updater` | rebuildbar | nein | bleibt bewusst in `frontend_net`, weil `backend_net` internal ist |
## Public / User Apps ## Public / User Apps
| Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs | | Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs |
|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|
| `paperless-ngx` | Dokumentenmanagement | `apps/paperless/docker-compose.yml` | `https://paperless.kaleschke.info` | PostgreSQL 17, Redis, Traefik | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/scans_inbox` | Tier 2, Borg + `postgresql17-paperless.dump` | ja | DB/Redis Secrets bleiben bewusst Stack ENV | | `paperless-ngx` | Dokumentenmanagement | `apps/paperless/docker-compose.yml` | `https://paperless.kaleschke.info` | PostgreSQL 18, Redis 8, Traefik | `/mnt/user/appdata/paperless-ngx/data`, `/mnt/user/documents/paperless`, `/mnt/user/documents/scans_inbox` | Tier 2, Borg + `postgresql17-paperless.dump` | ja | DB/Redis Secrets bleiben bewusst Stack ENV; Dump-Dateiname behaelt den historischen Cluster-Namen |
| `paperless-gpt` | KI-Ergaenzung fuer Paperless | `apps/paperless-gpt/docker-compose.yml` | `https://paperless-gpt.kaleschke.info` | Paperless API, LLM/Ollama, Traefik | `/mnt/user/appdata/paperless-gpt/data`, `/mnt/user/appdata/paperless-gpt/prompts` | Tier 2 | ja + Authelia | API Token als Stack ENV; OCR/LLM-Konfig bei Aenderungen pruefen. **Behalten-Entscheidung 2026-05-28:** Container bleibt aktiv, auch wenn aktuell keine Traefik-Zugriffe in der Woche; Ablouseplanung erst mit Paperless-NGX 3.0 (eigene KI-Features erwartet) - dann neu bewerten. | | `paperless-gpt` | KI-Ergaenzung fuer Paperless | `apps/paperless-gpt/docker-compose.yml` | `https://paperless-gpt.kaleschke.info` | Paperless API, LLM/Ollama, Traefik | `/mnt/user/appdata/paperless-gpt/data`, `/mnt/user/appdata/paperless-gpt/prompts` | Tier 2 | ja + Authelia | API Token als Stack ENV; OCR/LLM-Konfig bei Aenderungen pruefen. **Behalten-Entscheidung 2026-05-28:** Container bleibt aktiv, auch wenn aktuell keine Traefik-Zugriffe in der Woche; Ablouseplanung erst mit Paperless-NGX 3.0 (eigene KI-Features erwartet) - dann neu bewerten. |
| `immich_server` | Foto-/Video-App | `apps/immich/docker-compose.yml` | `https://immich.kaleschke.info` | Immich Postgres, Immich Redis, ML, Traefik | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` | Tier 2, Borg + `immich.dump` | ja | native App-Auth; externes Fotoarchiv gemountet | | `immich_server` | Foto-/Video-App | `apps/immich/docker-compose.yml` | `https://immich.kaleschke.info` | Immich Postgres, Immich Redis, ML, Traefik | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` | Tier 2, Borg + `immich.dump` | ja | native App-Auth; externes Fotoarchiv gemountet |
| `immich_postgres` | Immich-Datenbank | `apps/immich/docker-compose.yml` | intern | `immich_default` | `/mnt/user/appdata/immich_postgres`, `immich_postgres_password.txt` | Dump `immich.dump` | nein | nie ins `frontend_net` | | `immich_postgres` | Immich-Datenbank | `apps/immich/docker-compose.yml` | intern | `immich_default` | `/mnt/user/appdata/immich_postgres_vectorchord`, Rollback-Altstand `/mnt/user/appdata/immich_postgres`, `immich_postgres_password.txt` | Dump `immich.dump`; Restore braucht ein Image mit VectorChord/pgvector | nein | PG14 bleibt bewusst; Immich-DB-Image `ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0`; nie ins `frontend_net` |
| `immich_redis` | Immich Cache | `apps/immich/docker-compose.yml` | intern | `immich_default` | kein kritischer Pfad dokumentiert | rebuildbar | nein | Architektur nennt anonymes Volume -> named volume als offenes Thema | | `immich_redis` | Immich Cache | `apps/immich/docker-compose.yml` | intern | `immich_default` | kein kritischer Pfad dokumentiert | rebuildbar | nein | Redis 8.8; Architektur nennt anonymes Volume -> named volume als offenes Thema |
| `immich_machine_learning` | Immich ML | `apps/immich/docker-compose.yml` | intern | `immich_default` | `model-cache` | rebuildbar | nein | intern-only | | `immich_machine_learning` | Immich ML | `apps/immich/docker-compose.yml` | intern | `immich_default` | `model-cache` | rebuildbar | nein | intern-only |
| `mealie` | Rezeptverwaltung | `apps/mealie/docker-compose.yml` | `https://mealie.kaleschke.info` | `mealie-postgres`, Traefik | `/mnt/user/appdata/mealie/data` | Tier 2, Borg + `mealie.dump` | ja | App + DB in internem Netz getrennt | | `mealie` | Rezeptverwaltung | `apps/mealie/docker-compose.yml` | `https://mealie.kaleschke.info` | `mealie-postgres`, Traefik | `/mnt/user/appdata/mealie/data` | Tier 2, Borg + `mealie.dump` | ja | App + DB in internem Netz getrennt |
| `mealie-postgres` | Mealie-Datenbank | `apps/mealie/docker-compose.yml` | intern | `mealie_internal` | `/mnt/user/appdata/mealie/postgres`, `mealie_postgres_password.txt` | Dump `mealie.dump` | nein | interne DB | | `mealie-postgres` | Mealie-Datenbank | `apps/mealie/docker-compose.yml` | intern | `mealie_internal` | `/mnt/user/appdata/mealie/postgres18`, Rollback-Altstand `/mnt/user/appdata/mealie/postgres`, `mealie_postgres_password.txt` | Dump `mealie.dump` | nein | interne DB; PostgreSQL 18 |
| `mail-archiver` | Mail-Archivierung | `apps/mail-archiver/docker-compose.yml` | `https://mail.kaleschke.info` | PostgreSQL 17, Internet/IMAP, Traefik, Authelia | `/mnt/user/appdata/mailarchiver/data-protection-keys` | Tier 2, `postgresql17-mailarchiver.dump` | ja + Authelia | Hybrid-Dienst: `frontend_net` fuer Internet, `backend_net` fuer DB; App-eigene Auth bleibt zusaetzliche Schutzschicht | | `mail-archiver` | Mail-Archivierung | `apps/mail-archiver/docker-compose.yml` | `https://mail.kaleschke.info` | PostgreSQL 18, Internet/IMAP, Traefik, Authelia | `/mnt/user/appdata/mailarchiver/data-protection-keys` | Tier 2, `postgresql17-mailarchiver.dump` | ja + Authelia | Hybrid-Dienst: `frontend_net` fuer Internet, `backend_net` fuer DB; App-eigene Auth bleibt zusaetzliche Schutzschicht; Dump-Dateiname behaelt den historischen Cluster-Namen |
| `nextcloud` | Datei-/Cloud-Dienst | `apps/nextcloud/docker-compose.yml` | `https://cloud.kaleschke.info` | eigene PostgreSQL, eigene Redis, Traefik | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | Tier 2, `nextcloud.dump` + Share | ja | native App-Auth ohne zentrale ForwardAuth; WebDAV/CardDAV beachten | | `nextcloud` | Datei-/Cloud-Dienst | `apps/nextcloud/docker-compose.yml` | `https://cloud.kaleschke.info` | eigene PostgreSQL, eigene Redis, Traefik | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | Tier 2, `nextcloud.dump` + Share | ja | native App-Auth ohne zentrale ForwardAuth; WebDAV/CardDAV beachten |
| `nextcloud-postgres` | Nextcloud-Datenbank | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/postgres`, `nextcloud_postgres_password.txt` | `nextcloud.dump`, raw DB nicht primaerer Restore-Weg | nein | interne DB | | `nextcloud-postgres` | Nextcloud-Datenbank | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/postgres18`, Rollback-Altstand `/mnt/user/appdata/nextcloud/postgres`, `nextcloud_postgres_password.txt` | `nextcloud.dump`, raw DB nicht primaerer Restore-Weg | nein | interne DB; PostgreSQL 18 |
| `nextcloud-redis` | Nextcloud Cache/Locking | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/redis` | Teil von Nextcloud-Restore | nein | interne Redis | | `nextcloud-redis` | Nextcloud Cache/Locking | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/redis` | Teil von Nextcloud-Restore | nein | interne Redis 8.8 |
| `plex` | Medienserver mit LAN-/Client-Discovery | `host-services/plex/docker-compose.yml` | Plex native, **LAN/Tailscale-only**, Remote Access deaktiviert | Host-Netz | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` | Tier 2, Appdata + Medienpfade im Borg-/Share-Scope | nein | Repo-Compose-Stack; `network_mode: host` bleibt dokumentierte Discovery-Ausnahme. Server geclaimt von `Xeridos` (Reclaim 2026-05-28 nach Preferences-Reset vom 18.05.). Smart-TVs greifen ueber WLAN-LAN per mDNS/Plex-GDM direkt zu. `PublishServerOnPlexOnlineKey=0` (Remote Access aus), `RelayEnabled` ebenfalls aus. | | `plex` | Medienserver mit LAN-/Client-Discovery | `host-services/plex/docker-compose.yml` | Plex native, **LAN/Tailscale-only**, Remote Access deaktiviert | Host-Netz | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` | Tier 2, Appdata + Medienpfade im Borg-/Share-Scope | nein | Repo-Compose-Stack; `network_mode: host` bleibt dokumentierte Discovery-Ausnahme. Server geclaimt von `Xeridos` (Reclaim 2026-05-28 nach Preferences-Reset vom 18.05.). Smart-TVs greifen ueber WLAN-LAN per mDNS/Plex-GDM direkt zu. `PublishServerOnPlexOnlineKey=0` (Remote Access aus), `RelayEnabled` ebenfalls aus. |
| `ntfy` | Push-Benachrichtigungen | `apps/ntfy/docker-compose.yml` | `https://ntfy.kaleschke.info` | Traefik, upstream mobile push | `/mnt/user/appdata/ntfy` | Tier 2 | ja | `NTFY_BEHIND_PROXY=true`; Problem-Alerts gehen gebuendelt an `homelab-alerts`, optionale Erfolgsmeldungen an `homelab-info` | | `ntfy` | Push-Benachrichtigungen | `apps/ntfy/docker-compose.yml` | `https://ntfy.kaleschke.info` | Traefik, upstream mobile push | `/mnt/user/appdata/ntfy` | Tier 2 | ja | `NTFY_BEHIND_PROXY=true`; Problem-Alerts gehen gebuendelt an `homelab-alerts`, optionale Erfolgsmeldungen an `homelab-info` |
| `bentopdf` | PDF-Tooling / Ersatz fuer Stirling-PDF | `apps/bentopdf/docker-compose.yml` | `https://pdf.kaleschke.info` | Traefik + Authelia | keine kritische Persistenz im Compose | Tier 3, rebuildbar | ja + Authelia | COOP/COEP per Middleware. **Behalten-Entscheidung 2026-05-28:** Container bleibt aktiv als situatives Tool, auch wenn aktuell keine Traefik-Zugriffe in der Woche. Resource-Footprint vernachlaessigbar (~4 MB RAM). | | `bentopdf` | PDF-Tooling / Ersatz fuer Stirling-PDF | `apps/bentopdf/docker-compose.yml` | `https://pdf.kaleschke.info` | Traefik + Authelia | keine kritische Persistenz im Compose | Tier 3, rebuildbar | ja + Authelia | COOP/COEP per Middleware. **Behalten-Entscheidung 2026-05-28:** Container bleibt aktiv als situatives Tool, auch wenn aktuell keine Traefik-Zugriffe in der Woche. Resource-Footprint vernachlaessigbar (~4 MB RAM). |
+3 -3
View File
@@ -1,6 +1,6 @@
services: services:
postgresql17: postgresql17:
image: postgres:17.10@sha256:0027bef26712baaee437a4ea48fdf3d2d2e2bc5f0d81615374408ca320f3c7e3 image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39
container_name: postgresql17 container_name: postgresql17
restart: unless-stopped restart: unless-stopped
@@ -9,10 +9,10 @@ services:
POSTGRES_USER: mailarchiver POSTGRES_USER: mailarchiver
POSTGRES_DB: mailarchiver POSTGRES_DB: mailarchiver
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
PGDATA: /var/lib/postgresql/data PGDATA: /var/lib/postgresql/18/docker
volumes: volumes:
- /mnt/user/appdata/postgresql17:/var/lib/postgresql/data - /mnt/user/appdata/postgresql18:/var/lib/postgresql
- /mnt/user/appdata/secrets/postgres_password.txt:/run/secrets/postgres_password:ro - /mnt/user/appdata/secrets/postgres_password.txt:/run/secrets/postgres_password:ro
networks: networks:
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
redis: redis:
image: redis:7.4-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99 image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1
container_name: Redis container_name: Redis
restart: unless-stopped restart: unless-stopped
command: command:
+6 -2
View File
@@ -1,6 +1,6 @@
# Borg Backup Scope for KalliLabcore # Borg Backup Scope for KalliLabcore
Stand: 2026-05-16 Stand: 2026-05-31
This file defines the target state for replacing Backrest with Borg in this homelab. This file defines the target state for replacing Backrest with Borg in this homelab.
@@ -69,7 +69,7 @@ The live Unraid User Scripts execute repo scripts from `/mnt/user/services/homel
## Database Dumps Required ## Database Dumps Required
### Shared PostgreSQL (`postgresql17`) ### Shared PostgreSQL (`postgresql17`, runtime PostgreSQL 18)
- `mailarchiver` - `mailarchiver`
- `paperless` - `paperless`
@@ -91,9 +91,13 @@ The live Unraid User Scripts execute repo scripts from `/mnt/user/services/homel
## Explicitly Not Backed Up as Raw Live DB Files ## Explicitly Not Backed Up as Raw Live DB Files
- `/mnt/user/appdata/postgresql17` - `/mnt/user/appdata/postgresql17`
- `/mnt/user/appdata/postgresql18`
- `/mnt/user/appdata/mealie/postgres` - `/mnt/user/appdata/mealie/postgres`
- `/mnt/user/appdata/mealie/postgres18`
- `/mnt/user/appdata/immich_postgres` - `/mnt/user/appdata/immich_postgres`
- `/mnt/user/appdata/immich_postgres_vectorchord`
- `/mnt/user/appdata/nextcloud/postgres` - `/mnt/user/appdata/nextcloud/postgres`
- `/mnt/user/appdata/nextcloud/postgres18`
- `/mnt/user/appdata/komodo/mongo` - `/mnt/user/appdata/komodo/mongo`
- `/mnt/user/appdata/redis` - `/mnt/user/appdata/redis`
- `/mnt/user/appdata/scrutiny/influxdb` - `/mnt/user/appdata/scrutiny/influxdb`
+1 -1
View File
@@ -278,7 +278,7 @@ main() {
need_cmd sha256sum need_cmd sha256sum
ensure_dirs ensure_dirs
# Shared PostgreSQL 17 # Shared PostgreSQL 18 (historischer Containername: postgresql17)
if need_container "postgresql17"; then if need_container "postgresql17"; then
# Use the cluster admin/superuser for all shared-cluster dumps. The # Use the cluster admin/superuser for all shared-cluster dumps. The
# application roles exist, but they can have different passwords from the # application roles exist, but they can have different passwords from the
+1 -1
View File
@@ -473,7 +473,7 @@ pages:
category: core category: core
hide: false hide: false
postgresql17: postgresql17:
name: PostgreSQL 17 name: PostgreSQL 18
icon: si:postgresql icon: si:postgresql
description: Shared DB description: Shared DB
category: core category: core
+7 -7
View File
@@ -90,16 +90,16 @@
"notes": "ADMIN_TOKEN_FILE; keine direkten Host-Ports" "notes": "ADMIN_TOKEN_FILE; keine direkten Host-Ports"
}, },
"postgresql17": { "postgresql17": {
"description": "Shared PostgreSQL Cluster", "description": "Shared PostgreSQL 18 Cluster (historischer Containername)",
"tier": 1, "tier": 1,
"category": "infra", "category": "infra",
"container_name": "postgresql17", "container_name": "postgresql17",
"dependencies": [], "dependencies": [],
"url": null, "url": null,
"dump_file": null, "dump_file": null,
"data_paths": ["/mnt/user/appdata/postgresql17"], "data_paths": ["/mnt/user/appdata/postgresql18"],
"first_check": "backend_net Konnektivitaet? Disk-Space auf /mnt/user/appdata? pg_isready im Container?", "first_check": "backend_net Konnektivitaet? Disk-Space auf /mnt/user/appdata? pg_isready im Container?",
"notes": "Dumps per Dienst unter dumps/latest; raw DB nicht primaerer Restore-Weg" "notes": "Dumps per Dienst unter dumps/latest; raw DB nicht primaerer Restore-Weg; alter PG17-Pfad bleibt nur Rollback-Altstand"
}, },
"komodo-core": { "komodo-core": {
"description": "GitOps UI / API / Stack-Manager", "description": "GitOps UI / API / Stack-Manager",
@@ -200,9 +200,9 @@
"dependencies": [], "dependencies": [],
"url": null, "url": null,
"dump_file": "immich.dump", "dump_file": "immich.dump",
"data_paths": ["/mnt/user/appdata/immich_postgres"], "data_paths": ["/mnt/user/appdata/immich_postgres_vectorchord"],
"first_check": "immich_default Netz? Disk-Space? pg_isready?", "first_check": "immich_default Netz? Disk-Space? pg_isready?",
"notes": "nie ins frontend_net; immich_default Netz isoliert" "notes": "PG14 mit VectorChord/pgvector; nie ins frontend_net; immich_default Netz isoliert; alter immich_postgres-Pfad bleibt nur Rollback-Altstand"
}, },
"immich_redis": { "immich_redis": {
"description": "Immich Cache", "description": "Immich Cache",
@@ -248,7 +248,7 @@
"dependencies": [], "dependencies": [],
"url": null, "url": null,
"dump_file": "mealie.dump", "dump_file": "mealie.dump",
"data_paths": ["/mnt/user/appdata/mealie/postgres"], "data_paths": ["/mnt/user/appdata/mealie/postgres18"],
"first_check": "mealie_internal Netz? Disk-Space?", "first_check": "mealie_internal Netz? Disk-Space?",
"notes": "interne DB; mealie_internal Netz" "notes": "interne DB; mealie_internal Netz"
}, },
@@ -287,7 +287,7 @@
"dependencies": [], "dependencies": [],
"url": null, "url": null,
"dump_file": null, "dump_file": null,
"data_paths": ["/mnt/user/appdata/nextcloud/postgres"], "data_paths": ["/mnt/user/appdata/nextcloud/postgres18"],
"first_check": "nextcloud_internal Netz? Disk-Space?", "first_check": "nextcloud_internal Netz? Disk-Space?",
"notes": "interne DB" "notes": "interne DB"
}, },
+8 -8
View File
@@ -1,7 +1,7 @@
# services.yaml — Maschinenlesbare Wissensbasis fuer Hermes Alert Enrichment # services.yaml — Maschinenlesbare Wissensbasis fuer Hermes Alert Enrichment
# #
# Abgeleitet aus docs/SERVICE_CATALOG.md # Abgeleitet aus docs/SERVICE_CATALOG.md
# Stand: 2026-05-06 # Stand: 2026-05-31
# #
# Zweck: Hermes laedt diese Datei beim Alert-Anreichern, um Abhaengigkeiten, # Zweck: Hermes laedt diese Datei beim Alert-Anreichern, um Abhaengigkeiten,
# Dump-Zeitstempel und den ersten Diagnoseschritt nachzuschlagen. # Dump-Zeitstempel und den ersten Diagnoseschritt nachzuschlagen.
@@ -128,7 +128,7 @@ services:
notes: "ADMIN_TOKEN_FILE; keine direkten Host-Ports" notes: "ADMIN_TOKEN_FILE; keine direkten Host-Ports"
postgresql17: postgresql17:
description: Shared PostgreSQL Cluster (Authelia, Paperless, Mail-Archiver, Mealie, Komodo indirekt) description: Shared PostgreSQL 18 Cluster (historischer Containername; Authelia, Paperless, Mail-Archiver)
tier: 1 tier: 1
category: infra category: infra
container_name: postgresql17 container_name: postgresql17
@@ -136,9 +136,9 @@ services:
url: null url: null
dump_file: null dump_file: null
data_paths: data_paths:
- /mnt/user/appdata/postgresql17 - /mnt/user/appdata/postgresql18
first_check: "backend_net Konnektivitaet? Disk-Space auf /mnt/user/appdata? pg_isready im Container?" first_check: "backend_net Konnektivitaet? Disk-Space auf /mnt/user/appdata? pg_isready im Container?"
notes: "Dumps per Dienst unter dumps/latest; raw DB nicht primaerer Restore-Weg" notes: "Dumps per Dienst unter dumps/latest; raw DB nicht primaerer Restore-Weg; alter PG17-Pfad bleibt nur Rollback-Altstand"
komodo-core: komodo-core:
description: GitOps UI / API / Stack-Manager description: GitOps UI / API / Stack-Manager
@@ -261,9 +261,9 @@ services:
url: null url: null
dump_file: immich.dump dump_file: immich.dump
data_paths: data_paths:
- /mnt/user/appdata/immich_postgres - /mnt/user/appdata/immich_postgres_vectorchord
first_check: "immich_default Netz? Disk-Space? pg_isready?" first_check: "immich_default Netz? Disk-Space? pg_isready?"
notes: "nie ins frontend_net; immich_default Netz isoliert" notes: "PG14 mit VectorChord/pgvector; nie ins frontend_net; immich_default Netz isoliert; alter immich_postgres-Pfad bleibt nur Rollback-Altstand"
immich_redis: immich_redis:
description: Immich Cache description: Immich Cache
@@ -314,7 +314,7 @@ services:
url: null url: null
dump_file: mealie.dump dump_file: mealie.dump
data_paths: data_paths:
- /mnt/user/appdata/mealie/postgres - /mnt/user/appdata/mealie/postgres18
first_check: "mealie_internal Netz? Disk-Space?" first_check: "mealie_internal Netz? Disk-Space?"
notes: "interne DB; mealie_internal Netz" notes: "interne DB; mealie_internal Netz"
@@ -360,7 +360,7 @@ services:
url: null url: null
dump_file: null dump_file: null
data_paths: data_paths:
- /mnt/user/appdata/nextcloud/postgres - /mnt/user/appdata/nextcloud/postgres18
first_check: "nextcloud_internal Netz? Disk-Space?" first_check: "nextcloud_internal Netz? Disk-Space?"
notes: "interne DB" notes: "interne DB"
+3 -3
View File
@@ -36,7 +36,7 @@ Ziel:
- `immich-restore-test.sh`: hosttauglicher Immich-Restore-Job, erster echter Lauf noch offen - `immich-restore-test.sh`: hosttauglicher Immich-Restore-Job, erster echter Lauf noch offen
- `immich-plan.md`: konkreter Immich-Testplan - `immich-plan.md`: konkreter Immich-Testplan
- `immich-runbook.md`: Operator-Runbook fuer den ersten Immich-Lauf - `immich-runbook.md`: Operator-Runbook fuer den ersten Immich-Lauf
- `immich-compose.test.yml`: isolierte Testinstanz fuer Immich inkl. pgvecto-rs Test-Postgres und Test-Redis - `immich-compose.test.yml`: isolierte Testinstanz fuer Immich inkl. VectorChord/pgvector-Test-Postgres und Test-Redis
- `check-restore-freshness.ps1`: woechentlicher Frische-Check fuer Dumps und Reports - `check-restore-freshness.ps1`: woechentlicher Frische-Check fuer Dumps und Reports
- `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs - `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs
- `check-restore-freshness.sh`: hosttauglicher Frische-Check - `check-restore-freshness.sh`: hosttauglicher Frische-Check
@@ -81,10 +81,10 @@ Aktuell ist das erste validierte Muster vorhanden.
- echter Vaultwarden-Restore am 2026-05-07 erfolgreich verifiziert - echter Vaultwarden-Restore am 2026-05-07 erfolgreich verifiziert
- echter Gitea-Restore am 2026-05-07 erfolgreich verifiziert - echter Gitea-Restore am 2026-05-07 erfolgreich verifiziert
- echter Paperless-Restore am 2026-05-07 erfolgreich verifiziert - echter Paperless-Restore am 2026-05-07 erfolgreich verifiziert
- Immich-Restore-Test am 2026-05-26 vorbereitet; erster echter Lauf mit Report steht noch aus - Immich-Restore-Test am 2026-05-27 erfolgreich verifiziert; Test-Postgres wurde nach der VectorChord-Migration am 2026-05-31 auf das produktive Immich-Postgres-Image umgestellt
- Bash-Dispatcher und Bash-Restore-Jobs am 2026-05-07 erfolgreich hostseitig verifiziert - Bash-Dispatcher und Bash-Restore-Jobs am 2026-05-07 erfolgreich hostseitig verifiziert
- Restore-Lab und Report-Pfade auf dem Host angelegt - Restore-Lab und Report-Pfade auf dem Host angelegt
- V1-Ablauf weiter ohne `ntfy`, mit Bereinigung nach Erfolg - V1-Ablauf weiter ohne `ntfy`, mit Bereinigung nach Erfolg
- naechster grosser Kandidat ist der erste echte Immich-Lauf mit Zeitmessung; erst danach in die Rotation aufnehmen - naechster grosser Kandidat ist ein erneuter Immich-Lauf nach VectorChord-Migration mit Zeitmessung; danach in die Rotation aufnehmen
Vor dem ersten echten Testlauf muessen Zielpfade, Quellpfade und Bereinigungsschritte bewusst freigegeben werden. Vor dem ersten echten Testlauf muessen Zielpfade, Quellpfade und Bereinigungsschritte bewusst freigegeben werden.
+5 -4
View File
@@ -1,8 +1,8 @@
services: services:
restoretest-immich-postgres: restoretest-immich-postgres:
# gleiches Image wie Produktion, damit pgvecto-rs / pgvector-Extensions # Gleiches DB-Image wie Produktion, damit VectorChord/pgvector beim
# beim Restore aus immich.dump verfuegbar sind # Restore aus immich.dump verfuegbar sind.
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
container_name: restoretest-immich-postgres container_name: restoretest-immich-postgres
restart: "no" restart: "no"
environment: environment:
@@ -11,6 +11,7 @@ services:
POSTGRES_DB: immich POSTGRES_DB: immich
POSTGRES_PASSWORD: restoretest-immich-db POSTGRES_PASSWORD: restoretest-immich-db
PGDATA: /var/lib/postgresql/data PGDATA: /var/lib/postgresql/data
shm_size: 128mb
volumes: volumes:
- /mnt/user/backups/restore-lab/immich/postgres:/var/lib/postgresql/data - /mnt/user/backups/restore-lab/immich/postgres:/var/lib/postgresql/data
healthcheck: healthcheck:
@@ -22,7 +23,7 @@ services:
- no-new-privileges:true - no-new-privileges:true
restoretest-immich-redis: restoretest-immich-redis:
image: redis:7.4-alpine image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1
container_name: restoretest-immich-redis container_name: restoretest-immich-redis
restart: "no" restart: "no"
command: command:
+8 -8
View File
@@ -8,7 +8,7 @@ Bewusst **nicht** Teil dieses Tests:
- Wiederherstellung produktiver Foto-Dateien aus `/mnt/user/photos/immich` und `/mnt/user/photos/family_archive`. Der Smoke-Test bleibt DB-/UI-zentriert. - Wiederherstellung produktiver Foto-Dateien aus `/mnt/user/photos/immich` und `/mnt/user/photos/family_archive`. Der Smoke-Test bleibt DB-/UI-zentriert.
- Machine-Learning-Container. Spart Image-Pull-Zeit und Resource-Last; ML-Features sind im Smoke-Test nicht erforderlich. - Machine-Learning-Container. Spart Image-Pull-Zeit und Resource-Last; ML-Features sind im Smoke-Test nicht erforderlich.
- Echte Browser-Login-Sequenz. Smoke-Test prueft nur, dass die Login-Seite ausgeliefert wird und die DB-Tabellen `assets` und `users` lesbar sind. - Echte Browser-Login-Sequenz. Smoke-Test prueft nur, dass die Login-Seite ausgeliefert wird und die DB-Tabellen `asset` und `"user"` lesbar sind.
## Quelle ## Quelle
@@ -27,8 +27,8 @@ Bewusst **nicht** Teil dieses Tests:
- `/mnt/user/backups/restore-lab/immich/dumps/latest/immich.dump` (extrahierter Dump) - `/mnt/user/backups/restore-lab/immich/dumps/latest/immich.dump` (extrahierter Dump)
- Testcontainer: - Testcontainer:
- `restoretest-immich-server` - `restoretest-immich-server`
- `restoretest-immich-postgres` (`tensorchord/pgvecto-rs:pg14-v0.2.0` - identisch zur Produktion, weil der Dump pgvecto-rs Extension referenziert) - `restoretest-immich-postgres` (`ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0` - identisch zur Produktion, weil VectorChord-Backups ein Image mit VectorChord brauchen)
- `restoretest-immich-redis` (`redis:7.4-alpine`, rebuildbar) - `restoretest-immich-redis` (`redis:8.8.0-alpine`, rebuildbar)
- Testport Web: `127.0.0.1:12283:2283` - Testport Web: `127.0.0.1:12283:2283`
- Report-Ziel: `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md` - Report-Ziel: `/mnt/user/backups/restore-reports/immich-YYYY-MM-DD.md`
@@ -46,7 +46,7 @@ Bewusst **nicht** Teil dieses Tests:
1. Restore-Ziel unter `/mnt/user/backups/restore-lab/immich` vorbereiten (postgres, upload, dumps/latest) 1. Restore-Ziel unter `/mnt/user/backups/restore-lab/immich` vorbereiten (postgres, upload, dumps/latest)
2. `local/borg-dumps/latest/immich.dump` aus dem aktuellsten Borg-Archiv extrahieren 2. `local/borg-dumps/latest/immich.dump` aus dem aktuellsten Borg-Archiv extrahieren
3. Test-Postgres (`pgvecto-rs`) und Test-Redis mit `ops/restore-tests/immich-compose.test.yml` starten 3. Test-Postgres (Immich-Postgres mit VectorChord) und Test-Redis mit `ops/restore-tests/immich-compose.test.yml` starten
4. `immich.dump` in Test-Postgres importieren (`pg_restore -Fc --clean --if-exists --no-owner --no-privileges`) 4. `immich.dump` in Test-Postgres importieren (`pg_restore -Fc --clean --if-exists --no-owner --no-privileges`)
5. Testinstanz `restoretest-immich-server` starten 5. Testinstanz `restoretest-immich-server` starten
6. lokalen Smoke-Test gegen `http://127.0.0.1:12283` ausfuehren und Asset/User-Count aus DB lesen 6. lokalen Smoke-Test gegen `http://127.0.0.1:12283` ausfuehren und Asset/User-Count aus DB lesen
@@ -61,12 +61,12 @@ Minimal erfolgreich:
- `pg_restore -Fc` laeuft ohne Fehler durch - `pg_restore -Fc` laeuft ohne Fehler durch
- Immich-Server liefert HTTP `200`, `302` oder `303` auf `/` - Immich-Server liefert HTTP `200`, `302` oder `303` auf `/`
- Response enthaelt mindestens einen der Marker `Immich`, `Login`, `Signin` - Response enthaelt mindestens einen der Marker `Immich`, `Login`, `Signin`
- `select count(*) from assets;` und `select count(*) from users;` sind lesbar - `select count(*) from asset;` und `select count(*) from "user";` sind lesbar
Optional spaeter: Optional spaeter:
- Echte Login-Form via API ansprechen - Echte Login-Form via API ansprechen
- pgvecto-rs Extension explizit per `\dx` pruefen - VectorChord-/pgvector-Extensions explizit per `\dx` pruefen
- Test mit gemountetem **read-only** Foto-Sample-Pfad und Thumbnail-Rendering - Test mit gemountetem **read-only** Foto-Sample-Pfad und Thumbnail-Rendering
- Test inkl. ML-Container, sobald genug Test-Ressourcen verfuegbar - Test inkl. ML-Container, sobald genug Test-Ressourcen verfuegbar
@@ -75,8 +75,8 @@ Optional spaeter:
| Risiko | Beschreibung | Mitigation | | Risiko | Beschreibung | Mitigation |
|---|---|---| |---|---|---|
| Dump-Groesse unbekannt | `pg_dump -Fc` der Immich-DB kann je nach Asset-/Face-Tabellen mehrere GB sein | Erster Lauf bewusst mit `--what-if`, anschliessend Operator-Test mit Zeitmessung | | Dump-Groesse unbekannt | `pg_dump -Fc` der Immich-DB kann je nach Asset-/Face-Tabellen mehrere GB sein | Erster Lauf bewusst mit `--what-if`, anschliessend Operator-Test mit Zeitmessung |
| `pg_restore`-Dauer unbekannt | Index-/Constraint-Aufbau und pgvecto-rs-Index-Build koennen lange dauern | Test-Postgres mit Health-Polling startet; Lauf nicht abbrechen ohne `pg_restore`-Exit | | `pg_restore`-Dauer unbekannt | Index-/Constraint-Aufbau und VectorChord-Index-Build koennen lange dauern | Test-Postgres mit Health-Polling startet; Lauf nicht abbrechen ohne `pg_restore`-Exit |
| pgvecto-rs Extension-Mismatch | Wenn das pgvecto-rs-Image im Test nicht exakt dieselbe Version wie Produktion ist, kann `CREATE EXTENSION vectors` im Restore fehlschlagen | Compose pinnt denselben Digest wie `apps/immich/docker-compose.yml` | | VectorChord-/pgvector-Extension-Mismatch | Wenn das Test-Postgres-Image nicht zu Produktion passt, kann der Restore oder Immich-Start fehlschlagen | Compose pinnt denselben Digest wie `apps/immich/docker-compose.yml` |
| Immich-Server-Migrations beim Start | Immich fuehrt beim ersten Start DB-Migrations aus; das kann nach Restore noch laufen, bevor Web-UI antwortet | Smoke-Test pollt HTTP bis zu 120 s, bevor er als Fehler markiert | | Immich-Server-Migrations beim Start | Immich fuehrt beim ersten Start DB-Migrations aus; das kann nach Restore noch laufen, bevor Web-UI antwortet | Smoke-Test pollt HTTP bis zu 120 s, bevor er als Fehler markiert |
| Asset-Files fehlen | Der Test mountet kein Foto-Volume; Immich zeigt "missing" auf Asset-Detail-Seiten | Smoke-Test prueft nur Login-Page und DB-Counts, nicht Asset-Rendering | | Asset-Files fehlen | Der Test mountet kein Foto-Volume; Immich zeigt "missing" auf Asset-Detail-Seiten | Smoke-Test prueft nur Login-Page und DB-Counts, nicht Asset-Rendering |
| ML-Endpoint unreachable | Immich-Server kann ML-Endpoint nicht erreichen | `IMMICH_MACHINE_LEARNING_URL` zeigt bewusst auf einen nicht erreichbaren Hostnamen; Login bleibt funktional, ML-Features bleiben deaktiviert | | ML-Endpoint unreachable | Immich-Server kann ML-Endpoint nicht erreichen | `IMMICH_MACHINE_LEARNING_URL` zeigt bewusst auf einen nicht erreichbaren Hostnamen; Login bleibt funktional, ML-Features bleiben deaktiviert |
+3 -3
View File
@@ -19,8 +19,8 @@ Write-Output "Expected Borg source paths inside archive:"
Write-Output " - local/borg-dumps/latest/immich.dump" Write-Output " - local/borg-dumps/latest/immich.dump"
Write-Output "" Write-Output ""
Write-Output "Planned isolation:" Write-Output "Planned isolation:"
Write-Output " - Test Postgres: tensorchord/pgvecto-rs:pg14-v0.2.0 (same as production)" Write-Output " - Test Postgres: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 (same as production)"
Write-Output " - Test Redis: redis:7.4-alpine (rebuildable, no restore needed)" Write-Output " - Test Redis: redis:8.8.0-alpine (rebuildable, no restore needed)"
Write-Output " - Test Server: ghcr.io/immich-app/immich-server:release (pinned digest like production)" Write-Output " - Test Server: ghcr.io/immich-app/immich-server:release (pinned digest like production)"
Write-Output " - ML container: deliberately omitted" Write-Output " - ML container: deliberately omitted"
Write-Output " - Test endpoint: 127.0.0.1:12283 (no Traefik, no public domain)" Write-Output " - Test endpoint: 127.0.0.1:12283 (no Traefik, no public domain)"
@@ -32,7 +32,7 @@ Write-Output "1. Prepare restore-lab target under /mnt/user/backups/restore-lab/
Write-Output "2. Extract immich.dump from current Borg archive into test path" Write-Output "2. Extract immich.dump from current Borg archive into test path"
Write-Output ' Template: borg extract "$BORG_REPO" "::ARCHIVE_NAME" local/borg-dumps/latest/immich.dump' Write-Output ' Template: borg extract "$BORG_REPO" "::ARCHIVE_NAME" local/borg-dumps/latest/immich.dump'
Write-Output ' Passphrase source: $(cat /mnt/user/appdata/secrets/borg_repo_passphrase.txt)' Write-Output ' Passphrase source: $(cat /mnt/user/appdata/secrets/borg_repo_passphrase.txt)'
Write-Output "3. Start isolated test Postgres (pgvecto-rs) and test Redis" Write-Output "3. Start isolated test Postgres (VectorChord/pgvector) and test Redis"
Write-Output "4. Import immich.dump into test Postgres with pg_restore -Fc --clean --if-exists --no-owner --no-privileges" Write-Output "4. Import immich.dump into test Postgres with pg_restore -Fc --clean --if-exists --no-owner --no-privileges"
Write-Output "5. Start restoretest-immich-server against isolated DB/Redis (ML omitted)" Write-Output "5. Start restoretest-immich-server against isolated DB/Redis (ML omitted)"
Write-Output "6. Run smoke checks against http://127.0.0.1:12283 and DB asset count" Write-Output "6. Run smoke checks against http://127.0.0.1:12283 and DB asset count"
+5 -5
View File
@@ -5,8 +5,8 @@ set -euo pipefail
# #
# Nicht-destruktiver Restore-Smoke-Test fuer Immich. # Nicht-destruktiver Restore-Smoke-Test fuer Immich.
# - liest immich.dump aus dem produktiven Borg-Archiv # - liest immich.dump aus dem produktiven Borg-Archiv
# - importiert in eine isolierte Test-Postgres-Instanz mit gleichem Image # - importiert in eine isolierte Test-Postgres-Instanz mit gleichem
# wie Produktion (tensorchord/pgvecto-rs) # VectorChord-Image wie Produktion
# - startet einen isolierten Immich-Server-Container ohne Traefik und # - startet einen isolierten Immich-Server-Container ohne Traefik und
# ohne ML-Container # ohne ML-Container
# - prueft Login-Page und Asset-Anzahl aus DB # - prueft Login-Page und Asset-Anzahl aus DB
@@ -43,8 +43,8 @@ ReportRoot: $REPORT_ROOT
Expected Borg source paths: Expected Borg source paths:
- local/borg-dumps/latest/immich.dump - local/borg-dumps/latest/immich.dump
Planned isolation: Planned isolation:
- Test-Postgres: tensorchord/pgvecto-rs:pg14-v0.2.0 - Test-Postgres: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
- Test-Redis: redis:7.4-alpine (rebuildbar, kein Restore) - Test-Redis: redis:8.8.0-alpine (rebuildbar, kein Restore)
- Test-Server: ghcr.io/immich-app/immich-server:release (Image-Pin wie Produktion) - Test-Server: ghcr.io/immich-app/immich-server:release (Image-Pin wie Produktion)
- ML-Container bewusst weggelassen - ML-Container bewusst weggelassen
- Test-Upload: leer, unter $RESTORE_ROOT/upload - Test-Upload: leer, unter $RESTORE_ROOT/upload
@@ -221,7 +221,7 @@ write_report "$REPORT_FILE" <<EOF
- Restore root: \`$RESTORE_ROOT\` - Restore root: \`$RESTORE_ROOT\`
- Test containers: - Test containers:
- \`restoretest-immich-server\` - \`restoretest-immich-server\`
- \`restoretest-immich-postgres\` (tensorchord/pgvecto-rs:pg14-v0.2.0) - \`restoretest-immich-postgres\` (ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0)
- \`restoretest-immich-redis\` - \`restoretest-immich-redis\`
- Test endpoint: \`http://127.0.0.1:12283\` - Test endpoint: \`http://127.0.0.1:12283\`
- ML container: deliberately omitted - ML container: deliberately omitted
+4 -4
View File
@@ -22,7 +22,7 @@ Vor dem ersten Lauf muss Operator entscheiden:
## Bestaetigter Host-Stand (Soll) ## Bestaetigter Host-Stand (Soll)
- produktiver Immich-Server: `immich_server` Container mit Image `ghcr.io/immich-app/immich-server:release@sha256:c15bff75068effb03f4355997d03dc7e0fc58720c2b54ad6f7f10d1bc57efaa5` - produktiver Immich-Server: `immich_server` Container mit Image `ghcr.io/immich-app/immich-server:release@sha256:c15bff75068effb03f4355997d03dc7e0fc58720c2b54ad6f7f10d1bc57efaa5`
- produktive Postgres: `immich_postgres` mit `tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52` - produktive Postgres: `immich_postgres` mit `ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23`
- produktive Foto-Pfade: `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` - produktive Foto-Pfade: `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive`
- aktueller Dump-Pfad: `/mnt/user/backups/borg/dumps/latest/immich.dump` - aktueller Dump-Pfad: `/mnt/user/backups/borg/dumps/latest/immich.dump`
- Secret: `/mnt/user/appdata/secrets/immich_postgres_password.txt` (wird vom Test **nicht** gebraucht; Test nutzt eigenes Test-Passwort) - Secret: `/mnt/user/appdata/secrets/immich_postgres_password.txt` (wird vom Test **nicht** gebraucht; Test nutzt eigenes Test-Passwort)
@@ -71,7 +71,7 @@ Minimal erwartet im Report:
- `Asset count in test DB`: Zahl, oder `n/a` bei Schema-Drift - `Asset count in test DB`: Zahl, oder `n/a` bei Schema-Drift
- `User count in test DB`: Zahl, oder `n/a` bei Schema-Drift - `User count in test DB`: Zahl, oder `n/a` bei Schema-Drift
- Pre-Dump-Hook-Kette (`pg_dump -Fc`) und Restore-Kette (`pg_restore -Fc`) sind kompatibel - Pre-Dump-Hook-Kette (`pg_dump -Fc`) und Restore-Kette (`pg_restore -Fc`) sind kompatibel
- pgvecto-rs Extension ist im Restore-DB sichtbar - VectorChord-/pgvector-Extensions sind in der Restore-DB sichtbar
Manuelle Folgepruefung (optional): Manuelle Folgepruefung (optional):
@@ -82,7 +82,7 @@ docker compose -f /mnt/user/services/homelab-infra/ops/restore-tests/immich-comp
restoretest-immich-postgres restoretest-immich-redis restoretest-immich-server restoretest-immich-postgres restoretest-immich-redis restoretest-immich-server
docker exec restoretest-immich-postgres psql -U immich -d immich -c "\dx" docker exec restoretest-immich-postgres psql -U immich -d immich -c "\dx"
docker exec restoretest-immich-postgres psql -U immich -d immich -tAc "select count(*) from assets;" docker exec restoretest-immich-postgres psql -U immich -d immich -tAc "select count(*) from asset;"
docker logs --tail 100 restoretest-immich-server docker logs --tail 100 restoretest-immich-server
docker compose -f /mnt/user/services/homelab-infra/ops/restore-tests/immich-compose.test.yml down docker compose -f /mnt/user/services/homelab-infra/ops/restore-tests/immich-compose.test.yml down
@@ -102,7 +102,7 @@ Das Skript bereinigt:
| Symptom | Ursache | Massnahme | | Symptom | Ursache | Massnahme |
|---|---|---| |---|---|---|
| `pg_restore: error: could not find extension ... vectors` | Test-Postgres-Image nicht pgvecto-rs | Compose-Pin im Test-Compose pruefen | | `pg_restore: error: could not find extension ... vector/vchord` | Test-Postgres-Image passt nicht zur Produktion | Compose-Pin im Test-Compose pruefen |
| HTTP-Timeout nach 120 s | Immich-Migrations laufen noch | Wartezeit im Skript erhoehen oder Logs pruefen | | HTTP-Timeout nach 120 s | Immich-Migrations laufen noch | Wartezeit im Skript erhoehen oder Logs pruefen |
| `pg_isready` nie healthy | Test-Postgres bricht beim Start ab (Datadir-Konflikt) | Restore-Lab vor Lauf vollstaendig leer; `docker logs restoretest-immich-postgres` | | `pg_isready` nie healthy | Test-Postgres bricht beim Start ab (Datadir-Konflikt) | Restore-Lab vor Lauf vollstaendig leer; `docker logs restoretest-immich-postgres` |
| Body matcht keine Marker | Immich UI hat sich versioniert; Marker-Liste anpassen | Marker im Skript erweitern (`grep -qiE`) | | Body matcht keine Marker | Immich UI hat sich versioniert; Marker-Liste anpassen | Marker im Skript erweitern (`grep -qiE`) |
+4 -4
View File
@@ -1,6 +1,6 @@
services: services:
restoretest-paperless-postgres: restoretest-paperless-postgres:
image: postgres:17.9@sha256:5b96f1a16bd9768b060dd2ffe55cb6225c4d9ef4d214a8b21eb08134869a97e4 image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39
container_name: restoretest-paperless-postgres container_name: restoretest-paperless-postgres
restart: "no" restart: "no"
environment: environment:
@@ -8,9 +8,9 @@ services:
POSTGRES_USER: paperless POSTGRES_USER: paperless
POSTGRES_DB: paperless POSTGRES_DB: paperless
POSTGRES_PASSWORD: restoretest-paperless-db POSTGRES_PASSWORD: restoretest-paperless-db
PGDATA: /var/lib/postgresql/data PGDATA: /var/lib/postgresql/18/docker
volumes: volumes:
- /mnt/user/backups/restore-lab/paperless/postgres:/var/lib/postgresql/data - /mnt/user/backups/restore-lab/paperless/postgres:/var/lib/postgresql
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U paperless -d paperless"] test: ["CMD-SHELL", "pg_isready -U paperless -d paperless"]
interval: 10s interval: 10s
@@ -20,7 +20,7 @@ services:
- no-new-privileges:true - no-new-privileges:true
restoretest-paperless-redis: restoretest-paperless-redis:
image: redis:7-alpine image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1
container_name: restoretest-paperless-redis container_name: restoretest-paperless-redis
restart: "no" restart: "no"
command: command:
+30 -1
View File
@@ -44,7 +44,7 @@
"postgres", "postgres",
"mongo", "mongo",
"redis", "redis",
"tensorchord/pgvecto-rs" "ghcr.io/immich-app/postgres"
], ],
"groupName": null, "groupName": null,
"automerge": false, "automerge": false,
@@ -57,6 +57,35 @@
"matchPackageNames": ["mongo"], "matchPackageNames": ["mongo"],
"allowedVersions": "/^8\\.0\\.\\d+$/" "allowedVersions": "/^8\\.0\\.\\d+$/"
}, },
{
"description": "Produktive PostgreSQL-Cluster bleiben nach Major-Upgrade auf der 18.x-Schiene",
"matchManagers": ["docker-compose"],
"matchFileNames": [
"infra/postgresql17/docker-compose.yml",
"apps/mealie/docker-compose.yml",
"apps/nextcloud/docker-compose.yml"
],
"matchPackageNames": ["postgres"],
"allowedVersions": "/^18\\.\\d+(?:-.+)?$/"
},
{
"description": "Immich-Postgres bleibt auf PG14 mit VectorChord/pgvector; kein generisches Postgres und kein PG-Major-Sprung",
"matchManagers": ["docker-compose"],
"matchFileNames": ["apps/immich/docker-compose.yml"],
"matchPackageNames": ["ghcr.io/immich-app/postgres"],
"allowedVersions": "/^14-vectorchord\\d+\\.\\d+\\.\\d+(?:-pgvectors\\d+\\.\\d+\\.\\d+)?$/"
},
{
"description": "Produktive Redis-Instanzen bleiben nach Major-Upgrade auf der 8.x-Schiene",
"matchManagers": ["docker-compose"],
"matchFileNames": [
"infra/redis/docker-compose.yml",
"apps/nextcloud/docker-compose.yml",
"apps/immich/docker-compose.yml"
],
"matchPackageNames": ["redis"],
"allowedVersions": "/^8\\.\\d+\\.\\d+-alpine(?:\\d+\\.\\d+)?$/"
},
{ {
"description": "Komodo Major-Tag (release :2 mit Digest-Pin) wird nicht von Renovate auf :3 hochgesetzt", "description": "Komodo Major-Tag (release :2 mit Digest-Pin) wird nicht von Renovate auf :3 hochgesetzt",
"matchPackageNames": [ "matchPackageNames": [