diff --git a/apps/immich/docker-compose.yml b/apps/immich/docker-compose.yml index 3b7db47..b99f9bf 100644 --- a/apps/immich/docker-compose.yml +++ b/apps/immich/docker-compose.yml @@ -43,7 +43,7 @@ services: redis: container_name: immich_redis - image: redis:7.4-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99 + image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1 restart: unless-stopped networks: - immich_default @@ -52,14 +52,15 @@ services: database: 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 environment: POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password POSTGRES_USER: immich POSTGRES_DB: immich + shm_size: 128mb 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 networks: - immich_default diff --git a/apps/mealie/docker-compose.yml b/apps/mealie/docker-compose.yml index 5a7c6cc..6e71d4e 100644 --- a/apps/mealie/docker-compose.yml +++ b/apps/mealie/docker-compose.yml @@ -38,7 +38,7 @@ services: - traefik.http.services.mealie.loadbalancer.server.port=9000 mealie-postgres: - image: postgres:17.10@sha256:0027bef26712baaee437a4ea48fdf3d2d2e2bc5f0d81615374408ca320f3c7e3 + image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39 container_name: mealie-postgres restart: unless-stopped @@ -47,10 +47,10 @@ services: POSTGRES_USER: mealie POSTGRES_DB: mealie POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password - PGDATA: /var/lib/postgresql/data + PGDATA: /var/lib/postgresql/18/docker 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 networks: diff --git a/apps/nextcloud/docker-compose.yml b/apps/nextcloud/docker-compose.yml index 95d0640..be0a91b 100644 --- a/apps/nextcloud/docker-compose.yml +++ b/apps/nextcloud/docker-compose.yml @@ -46,7 +46,7 @@ services: - "traefik.http.services.nextcloud.loadbalancer.server.port=80" nextcloud-postgres: - image: postgres:17.10@sha256:0027bef26712baaee437a4ea48fdf3d2d2e2bc5f0d81615374408ca320f3c7e3 + image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39 container_name: nextcloud-postgres restart: unless-stopped environment: @@ -54,9 +54,9 @@ services: POSTGRES_DB: nextcloud POSTGRES_USER: nextcloud POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password - PGDATA: /var/lib/postgresql/data + PGDATA: /var/lib/postgresql/18/docker 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 networks: - nextcloud_internal @@ -64,7 +64,7 @@ services: - no-new-privileges:true nextcloud-redis: - image: redis:7.4-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99 + image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1 container_name: nextcloud-redis restart: unless-stopped command: redis-server --save 60 1 --loglevel warning diff --git a/docs/DISASTER_RECOVERY.md b/docs/DISASTER_RECOVERY.md index 7c4626d..1c4fbf0 100644 --- a/docs/DISASTER_RECOVERY.md +++ b/docs/DISASTER_RECOVERY.md @@ -261,7 +261,7 @@ Ziel: ### 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/` 6. `infra/redis/` 7. `core/gitea/` @@ -396,6 +396,12 @@ Vor dem Start muessen vorhanden 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 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` - 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 nutzt einen lokalen Build und hostseitige Runtime-Daten. diff --git a/docs/IMMICH_RESTORE_TEST.md b/docs/IMMICH_RESTORE_TEST.md index e12bb20..a83211e 100644 --- a/docs/IMMICH_RESTORE_TEST.md +++ b/docs/IMMICH_RESTORE_TEST.md @@ -11,7 +11,7 @@ Schliesst die Audit-Luecke aus F-11: Immich ist der groesste Datentopf (Familien | 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.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 | @@ -20,7 +20,7 @@ Schliesst die Audit-Luecke aus F-11: Immich ist der groesste Datentopf (Familien ## Was der Test abdeckt - 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 - Smoke-Test: Login-Seite erreichbar, `asset`- und `"user"`-Tabelle lesbar - 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 - 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. - 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. @@ -66,8 +66,8 @@ Der Test deckt **Stufe 4 (kritische Anwendungen)** aus `docs/DISASTER_RECOVERY.m ## 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. -- pgvecto-rs 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. +- VectorChord-/pgvector-Extension-Mismatch bei Image-Drift moeglich; Compose pinnt denselben Digest wie Produktion. +- 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. - Bei Schema-Drift (z. B. nach Major-Update) koennen einzelne DB-Queries abweichen; das Skript versucht Immich-v2-Singular-Tabellen und aeltere Plural-Fallbacks. diff --git a/docs/RENOVATE.md b/docs/RENOVATE.md index d7dad29..af63c27 100644 --- a/docs/RENOVATE.md +++ b/docs/RENOVATE.md @@ -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 | | 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 | | 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 | @@ -105,12 +105,10 @@ Erstlauf 2026-05-29 erfolgreich: Renovate erzeugte das Dependency Dashboard und - `renovate/mongo-7.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 - #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. @@ -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: -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. 3. **Gruppe major-updates**: Erst nach Operator-Sichtpruefung pro Image, ggf. zurueckstellen oder manuell entscheiden. diff --git a/docs/REPO_MAP.md b/docs/REPO_MAP.md index 4300bff..09e594c 100644 --- a/docs/REPO_MAP.md +++ b/docs/REPO_MAP.md @@ -1,6 +1,6 @@ # 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. @@ -102,11 +102,11 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam | 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 | -| 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` | | 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 | -| 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 | +| 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: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 @@ -169,7 +169,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam | Network | Typ / Status | Nutzer | |---|---|---| | `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 | | `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 | @@ -189,13 +189,13 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam | Gitea | `/mnt/user/services/gitea/data` | | Authelia | `/mnt/user/appdata/authelia/config`, Authelia Secret-Dateien | | 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` | | 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` | -| Mealie | `/mnt/user/appdata/mealie/data`, `/mnt/user/appdata/mealie/postgres` | +| 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/postgres18`, Rollback-Altstand `/mnt/user/appdata/mealie/postgres` | | 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` | | ntfy | `/mnt/user/appdata/ntfy` | | 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` | | Authelia | JWT, Session, Storage Encryption, Postgres Password via `_FILE` | | Vaultwarden | `ADMIN_TOKEN_FILE` | -| PostgreSQL 17 | `POSTGRES_PASSWORD_FILE` | +| PostgreSQL 18 | `POSTGRES_PASSWORD_FILE` | | Redis | Passwort-Datei und Startkommando | | Paperless | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS` als Komodo Stack ENV | | 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. - `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. -- 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. - `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. diff --git a/docs/RESTORE_MATRIX.md b/docs/RESTORE_MATRIX.md index 34b8586..ec52a65 100644 --- a/docs/RESTORE_MATRIX.md +++ b/docs/RESTORE_MATRIX.md @@ -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 | | 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 | -| 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 | -| Redis | Share / Host | `/mnt/user/appdata/redis` | keine | `redis_password.txt` | `backend_net` | Redis startet, 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 | +| 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 8 | Share / Host | `/mnt/user/appdata/redis`; Rollback-Backup unter `/mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-` | 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 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 | | 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 | @@ -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 | |---|---|---|---|---|---|---| -| 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 | -| 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 | -| 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 | -| 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 | -| 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 | +| 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`, `/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`, `/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 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`, `/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-` | `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` | | 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 | @@ -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. +### 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 diff --git a/docs/SECRETS_MAP.md b/docs/SECRETS_MAP.md index b4cee43..3994007 100644 --- a/docs/SECRETS_MAP.md +++ b/docs/SECRETS_MAP.md @@ -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 | | 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 | | 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 | diff --git a/docs/SERVICE_CATALOG.md b/docs/SERVICE_CATALOG.md index a42322b..c5709d8 100644 --- a/docs/SERVICE_CATALOG.md +++ b/docs/SERVICE_CATALOG.md @@ -1,6 +1,6 @@ # 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. @@ -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 | |---|---|---|---|---|---|---|---|---| -| `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 | ## Shared Infrastructure | 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 | -| `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. | +| `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 | 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 | ## Public / User Apps | 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. | | `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_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_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 | 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 | | `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 | -| `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 | +| `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 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-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-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-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 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. | | `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). | diff --git a/infra/postgresql17/docker-compose.yml b/infra/postgresql17/docker-compose.yml index f46d85a..8972d86 100644 --- a/infra/postgresql17/docker-compose.yml +++ b/infra/postgresql17/docker-compose.yml @@ -1,6 +1,6 @@ services: postgresql17: - image: postgres:17.10@sha256:0027bef26712baaee437a4ea48fdf3d2d2e2bc5f0d81615374408ca320f3c7e3 + image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39 container_name: postgresql17 restart: unless-stopped @@ -9,10 +9,10 @@ services: POSTGRES_USER: mailarchiver POSTGRES_DB: mailarchiver POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password - PGDATA: /var/lib/postgresql/data + PGDATA: /var/lib/postgresql/18/docker 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 networks: diff --git a/infra/redis/docker-compose.yml b/infra/redis/docker-compose.yml index 01903e8..0cd82d6 100644 --- a/infra/redis/docker-compose.yml +++ b/infra/redis/docker-compose.yml @@ -1,6 +1,6 @@ services: redis: - image: redis:7.4-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99 + image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1 container_name: Redis restart: unless-stopped command: diff --git a/ops/borg-ui/BACKUP_SCOPE.md b/ops/borg-ui/BACKUP_SCOPE.md index 15b77a2..9452520 100644 --- a/ops/borg-ui/BACKUP_SCOPE.md +++ b/ops/borg-ui/BACKUP_SCOPE.md @@ -1,6 +1,6 @@ # 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. @@ -69,7 +69,7 @@ The live Unraid User Scripts execute repo scripts from `/mnt/user/services/homel ## Database Dumps Required -### Shared PostgreSQL (`postgresql17`) +### Shared PostgreSQL (`postgresql17`, runtime PostgreSQL 18) - `mailarchiver` - `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 - `/mnt/user/appdata/postgresql17` +- `/mnt/user/appdata/postgresql18` - `/mnt/user/appdata/mealie/postgres` +- `/mnt/user/appdata/mealie/postgres18` - `/mnt/user/appdata/immich_postgres` +- `/mnt/user/appdata/immich_postgres_vectorchord` - `/mnt/user/appdata/nextcloud/postgres` +- `/mnt/user/appdata/nextcloud/postgres18` - `/mnt/user/appdata/komodo/mongo` - `/mnt/user/appdata/redis` - `/mnt/user/appdata/scrutiny/influxdb` diff --git a/ops/borg-ui/scripts/pre-backup-dumps.sh b/ops/borg-ui/scripts/pre-backup-dumps.sh index f7ec53b..e4ff83f 100755 --- a/ops/borg-ui/scripts/pre-backup-dumps.sh +++ b/ops/borg-ui/scripts/pre-backup-dumps.sh @@ -278,7 +278,7 @@ main() { need_cmd sha256sum ensure_dirs - # Shared PostgreSQL 17 + # Shared PostgreSQL 18 (historischer Containername: postgresql17) if need_container "postgresql17"; then # Use the cluster admin/superuser for all shared-cluster dumps. The # application roles exist, but they can have different passwords from the diff --git a/ops/glance/config/glance.yml b/ops/glance/config/glance.yml index aae53c6..d3c2ae5 100644 --- a/ops/glance/config/glance.yml +++ b/ops/glance/config/glance.yml @@ -473,7 +473,7 @@ pages: category: core hide: false postgresql17: - name: PostgreSQL 17 + name: PostgreSQL 18 icon: si:postgresql description: Shared DB category: core diff --git a/ops/hermes-agent/services.json b/ops/hermes-agent/services.json index 8312e4f..47dad21 100644 --- a/ops/hermes-agent/services.json +++ b/ops/hermes-agent/services.json @@ -90,16 +90,16 @@ "notes": "ADMIN_TOKEN_FILE; keine direkten Host-Ports" }, "postgresql17": { - "description": "Shared PostgreSQL Cluster", + "description": "Shared PostgreSQL 18 Cluster (historischer Containername)", "tier": 1, "category": "infra", "container_name": "postgresql17", "dependencies": [], "url": 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?", - "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": { "description": "GitOps UI / API / Stack-Manager", @@ -200,9 +200,9 @@ "dependencies": [], "url": null, "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?", - "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": { "description": "Immich Cache", @@ -248,7 +248,7 @@ "dependencies": [], "url": null, "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?", "notes": "interne DB; mealie_internal Netz" }, @@ -287,7 +287,7 @@ "dependencies": [], "url": 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?", "notes": "interne DB" }, diff --git a/ops/hermes-agent/services.yaml b/ops/hermes-agent/services.yaml index 68cdfed..3ee7796 100644 --- a/ops/hermes-agent/services.yaml +++ b/ops/hermes-agent/services.yaml @@ -1,7 +1,7 @@ # services.yaml — Maschinenlesbare Wissensbasis fuer Hermes Alert Enrichment # # Abgeleitet aus docs/SERVICE_CATALOG.md -# Stand: 2026-05-06 +# Stand: 2026-05-31 # # Zweck: Hermes laedt diese Datei beim Alert-Anreichern, um Abhaengigkeiten, # Dump-Zeitstempel und den ersten Diagnoseschritt nachzuschlagen. @@ -128,7 +128,7 @@ services: notes: "ADMIN_TOKEN_FILE; keine direkten Host-Ports" 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 category: infra container_name: postgresql17 @@ -136,9 +136,9 @@ services: url: null dump_file: null 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?" - 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: description: GitOps UI / API / Stack-Manager @@ -261,9 +261,9 @@ services: url: null dump_file: immich.dump data_paths: - - /mnt/user/appdata/immich_postgres + - /mnt/user/appdata/immich_postgres_vectorchord 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: description: Immich Cache @@ -314,7 +314,7 @@ services: url: null dump_file: mealie.dump data_paths: - - /mnt/user/appdata/mealie/postgres + - /mnt/user/appdata/mealie/postgres18 first_check: "mealie_internal Netz? Disk-Space?" notes: "interne DB; mealie_internal Netz" @@ -360,7 +360,7 @@ services: url: null dump_file: null data_paths: - - /mnt/user/appdata/nextcloud/postgres + - /mnt/user/appdata/nextcloud/postgres18 first_check: "nextcloud_internal Netz? Disk-Space?" notes: "interne DB" diff --git a/ops/restore-tests/README.md b/ops/restore-tests/README.md index 658ca29..ce5d2b3 100644 --- a/ops/restore-tests/README.md +++ b/ops/restore-tests/README.md @@ -36,7 +36,7 @@ Ziel: - `immich-restore-test.sh`: hosttauglicher Immich-Restore-Job, erster echter Lauf noch offen - `immich-plan.md`: konkreter Immich-Testplan - `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 - `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs - `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 Gitea-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 - Restore-Lab und Report-Pfade auf dem Host angelegt - 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. diff --git a/ops/restore-tests/immich-compose.test.yml b/ops/restore-tests/immich-compose.test.yml index b2dd7d1..02c72d8 100644 --- a/ops/restore-tests/immich-compose.test.yml +++ b/ops/restore-tests/immich-compose.test.yml @@ -1,8 +1,8 @@ services: restoretest-immich-postgres: - # gleiches Image wie Produktion, damit pgvecto-rs / pgvector-Extensions - # beim Restore aus immich.dump verfuegbar sind - image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 + # Gleiches DB-Image wie Produktion, damit VectorChord/pgvector beim + # Restore aus immich.dump verfuegbar sind. + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23 container_name: restoretest-immich-postgres restart: "no" environment: @@ -11,6 +11,7 @@ services: POSTGRES_DB: immich POSTGRES_PASSWORD: restoretest-immich-db PGDATA: /var/lib/postgresql/data + shm_size: 128mb volumes: - /mnt/user/backups/restore-lab/immich/postgres:/var/lib/postgresql/data healthcheck: @@ -22,7 +23,7 @@ services: - no-new-privileges:true restoretest-immich-redis: - image: redis:7.4-alpine + image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1 container_name: restoretest-immich-redis restart: "no" command: diff --git a/ops/restore-tests/immich-plan.md b/ops/restore-tests/immich-plan.md index a03ac55..98a359c 100644 --- a/ops/restore-tests/immich-plan.md +++ b/ops/restore-tests/immich-plan.md @@ -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. - 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 @@ -27,8 +27,8 @@ Bewusst **nicht** Teil dieses Tests: - `/mnt/user/backups/restore-lab/immich/dumps/latest/immich.dump` (extrahierter Dump) - Testcontainer: - `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-redis` (`redis:7.4-alpine`, rebuildbar) + - `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:8.8.0-alpine`, rebuildbar) - Testport Web: `127.0.0.1:12283:2283` - 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) 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`) 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 @@ -61,12 +61,12 @@ Minimal erfolgreich: - `pg_restore -Fc` laeuft ohne Fehler durch - Immich-Server liefert HTTP `200`, `302` oder `303` auf `/` - 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: - 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 inkl. ML-Container, sobald genug Test-Ressourcen verfuegbar @@ -75,8 +75,8 @@ Optional spaeter: | 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 | -| `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 | -| 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` | +| `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 | +| 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 | | 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 | diff --git a/ops/restore-tests/immich-restore-test.ps1 b/ops/restore-tests/immich-restore-test.ps1 index 92ee497..8a59f83 100644 --- a/ops/restore-tests/immich-restore-test.ps1 +++ b/ops/restore-tests/immich-restore-test.ps1 @@ -19,8 +19,8 @@ Write-Output "Expected Borg source paths inside archive:" Write-Output " - local/borg-dumps/latest/immich.dump" Write-Output "" Write-Output "Planned isolation:" -Write-Output " - Test Postgres: tensorchord/pgvecto-rs:pg14-v0.2.0 (same as production)" -Write-Output " - Test Redis: redis:7.4-alpine (rebuildable, no restore needed)" +Write-Output " - Test Postgres: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 (same as production)" +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 " - ML container: deliberately omitted" 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 ' 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 "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 "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" diff --git a/ops/restore-tests/immich-restore-test.sh b/ops/restore-tests/immich-restore-test.sh index 3fbf0e7..9d6b211 100755 --- a/ops/restore-tests/immich-restore-test.sh +++ b/ops/restore-tests/immich-restore-test.sh @@ -5,8 +5,8 @@ set -euo pipefail # # Nicht-destruktiver Restore-Smoke-Test fuer Immich. # - liest immich.dump aus dem produktiven Borg-Archiv -# - importiert in eine isolierte Test-Postgres-Instanz mit gleichem Image -# wie Produktion (tensorchord/pgvecto-rs) +# - importiert in eine isolierte Test-Postgres-Instanz mit gleichem +# VectorChord-Image wie Produktion # - startet einen isolierten Immich-Server-Container ohne Traefik und # ohne ML-Container # - prueft Login-Page und Asset-Anzahl aus DB @@ -43,8 +43,8 @@ ReportRoot: $REPORT_ROOT Expected Borg source paths: - local/borg-dumps/latest/immich.dump Planned isolation: -- Test-Postgres: tensorchord/pgvecto-rs:pg14-v0.2.0 -- Test-Redis: redis:7.4-alpine (rebuildbar, kein Restore) +- Test-Postgres: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 +- Test-Redis: redis:8.8.0-alpine (rebuildbar, kein Restore) - Test-Server: ghcr.io/immich-app/immich-server:release (Image-Pin wie Produktion) - ML-Container bewusst weggelassen - Test-Upload: leer, unter $RESTORE_ROOT/upload @@ -221,7 +221,7 @@ write_report "$REPORT_FILE" <