diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index a089d71..068c8a2 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -53,6 +53,8 @@ services: # it falls back to it. TESSERA_ENCRYPTION_KEY: "${TESSERA_ENCRYPTION_KEY:-${CALENDAR_ENCRYPTION_KEY:?set TESSERA_ENCRYPTION_KEY in .env, generate one with openssl rand -hex 32}}" CALENDAR_ENCRYPTION_KEY: "${CALENDAR_ENCRYPTION_KEY:-}" + volumes: + - user-files:/app/user-files healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"] interval: 10s @@ -88,3 +90,4 @@ networks: volumes: pgdata: + user-files: diff --git a/docker-compose.yml b/docker-compose.yml index 0c819d1..303febc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -57,6 +57,8 @@ services: # it falls back to it. TESSERA_ENCRYPTION_KEY: "${TESSERA_ENCRYPTION_KEY:-${CALENDAR_ENCRYPTION_KEY:?set TESSERA_ENCRYPTION_KEY in .env, generate one with openssl rand -hex 32}}" CALENDAR_ENCRYPTION_KEY: "${CALENDAR_ENCRYPTION_KEY:-}" + volumes: + - user-files:/app/user-files healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"] interval: 10s @@ -91,3 +93,4 @@ networks: volumes: pgdata: + user-files: diff --git a/docs/anleitung-betrieb.md b/docs/anleitung-betrieb.md index 526dfc1..97308b5 100644 --- a/docs/anleitung-betrieb.md +++ b/docs/anleitung-betrieb.md @@ -270,24 +270,40 @@ Restores stattfinden. - **PostgreSQL-Daten**: liegen im benannten Docker-Volume `pgdata` (`docker-compose.prod.yml`, Mount `pgdata:/var/lib/postgresql/data` im - `db`-Container). Dieses Volume ist der einzige daürhafte Datenspeicher, der über - ein `docker volume`-Backup zusätzlich gesichert werden könnte. + `db`-Container). `pgdata` ist eines von zwei dauerhaften Docker-Volumes (siehe + nächster Punkt) und kann zusätzlich über ein `docker volume`-Backup gesichert + werden. - **Verschlüsselungsschlüssel** `TESSERA_ENCRYPTION_KEY`: liegt nur in `.env` auf dem Host, **nicht** im Datenbank-Dump. Getrennt sichern (siehe Kapitel 2/3) – ohne ihn sind alle per `pg_dump` gesicherten verschlüsselten Zugangsdaten wertlos. - **Hochgeladene Dateien** (Avatare unter `user-files/avatars/`, generierte DKV-Exporte unter `user-files/`, siehe `apps/api/src/user/user.controller.ts` und - `apps/api/src/dkv/dkv-export.service.ts`): Diese Dateien landen im Verzeichnis - `user-files/` **innerhalb** des `api`-Containers. Weder `docker-compose.yml` noch - `docker-compose.prod.yml` mounten dafür ein Docker-Volume oder ein Host-Verzeichnis - – der Ordner existiert ausschließlich in der beschreibbaren Container-Schicht. - **Das bedeutet: Bei jedem `--force-recreate` von `api` gehen Avatare und DKV-Exporte - verloren**, sofern auf dem Server nicht zusätzlich (außerhalb des Repo-Standes) - ein Bind-Mount ergänzt wurde. Vor einem Redeploy prüfen, ob auf dem Server ein - solcher Mount existiert (`docker inspect tessera-api-1` → `Mounts`); falls - nicht, gelten Avatare/Exporte als nicht persistent und sollten bei Bedarf vorher - manuell aus dem Container kopiert werden (`docker compose cp api:/app/user-files - ./user-files-backup`). + `apps/api/src/dkv/dkv-export.service.ts`): Diese Dateien liegen im benannten + Docker-Volume `user-files`, gemountet auf `/app/user-files` im Dienst `api`. Der + Mount ist in `docker-compose.yml` und `docker-compose.prod.yml` eingetragen: + + ```yaml + # beim Dienst api: + - user-files:/app/user-files + # im Top-Level-Block volumes: + user-files: + ``` + + Damit überstehen Avatare und DKV-Exporte ein `--force-recreate` von `api`. Wie bei + `pgdata` zeigt `docker volume ls` das Volume mit vorangestelltem Projektnamen an + (`_user-files`). Gesichert werden die Dateien weiterhin mit + `docker compose cp api:/app/user-files ./user-files-backup`, alternativ über eine + Sicherung des Docker-Volumes selbst (wie bei `pgdata`). + + **Wichtig für den Betrieb auf einem bestehenden Server:** `/opt/tessera/docker-compose.yml` + ist keine Arbeitskopie dieses Repositorys – die Datei wurde dort von Hand + bearbeitet und weicht ab. Ein Deploy holt ausschließlich Images und fasst diese + Datei nicht an. Diese Compose-Änderung erreicht eine bereits laufende Installation + deshalb **nicht von selbst**. Wer die Reparatur dort haben will, trägt dieselben + zwei Zeilen selbst in `/opt/tessera/docker-compose.yml` ein (vorher sichern) und + erstellt die Container einmal neu. Bis dahin gilt für die laufende Installation + weiterhin der alte, verlustbehaftete Zustand – prüfbar mit `docker inspect + tessera-api-1` und einem Blick auf `Mounts`. ## 7. Protokolle und Fehlersuche @@ -313,7 +329,7 @@ protokolliert die durchgelaufenen Prisma-Migrationen sowie zuletzt | Neue Version scheint nicht anzukommen, obwohl `pull` gelaufen ist | Klassische `up -d`-Falle ohne `--force-recreate` (siehe Kapitel 4) | `StartedAt` des Containers gegen `Created` des Images vergleichen, ggf. `--force-recreate` nachholen. | | Initialer Admin-Login funktioniert nicht nach Änderung von `TESSERA_ADMIN_PASSWORD` | Seed läuft nur, wenn der Benutzername noch **nicht** existiert; bestehende Accounts werden nicht überschrieben | Passwort über die Anwendung selbst (bzw. direkt in der Datenbank) ändern, nicht über die `.env`-Variable. | | Mails werden nicht versendet | `TESSERA_SMTP_HOST` leer (Prod-Default) | SMTP-Variablen vollständig setzen und Container neu erstellen. | -| Avatare/DKV-Exporte nach einem Deploy verschwunden | `user-files/` ist nicht als Volume gemountet, siehe Kapitel 6 | Vor `--force-recreate` sichern, langfristig einen Bind-Mount für `user-files/` ergänzen. | +| Avatare/DKV-Exporte nach einem Deploy verschwunden | Die verwendete Compose-Datei mountet `user-files/` nicht als Volume – im Repository-Stand seit dieser Version behoben, betrifft nur eine Installation mit abweichender Compose-Datei | Die zwei Zeilen aus Kapitel 6 in die verwendete Compose-Datei eintragen (auf dem Server: `/opt/tessera/docker-compose.yml`, vorher sichern) und `api` neu erstellen. | ## 8. Abgrenzung zur CI/CD-Pipeline