fix(compose): user-files dauerhaft speichern und Betriebshandbuch nachziehen

- docker-compose.yml und docker-compose.prod.yml mounten /app/user-files
  im Dienst api auf ein neues benanntes Volume user-files (Eigentuemerschaft
  uid 1001 folgt aus dem Image, kein Bind-Mount)
- docker-compose.dev.yml bleibt unveraendert (Compose fuehrt Mount-Listen
  ueber das Ziel zusammen)
- Betriebshandbuch Kapitel 6: Speicher als dauerhaft beschrieben, Mount-Zeile
  woertlich zum Kopieren, Hinweis dass /opt/tessera/docker-compose.yml auf
  dem Server separat gepflegt werden muss (Deploy erreicht sie nicht)
- Betriebshandbuch Kapitel 7: Fehlerzeile zu verschwundenen Avataren/Exporten
  an den reparierten Repository-Stand angepasst

WINDOWS #17 bleibt offen, bis die Serverdatei manuell ergaenzt ist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
This commit is contained in:
2026-09-09 09:34:15 +02:00
parent 7b49747c72
commit dab72eb1f9
3 changed files with 36 additions and 14 deletions
+3
View File
@@ -53,6 +53,8 @@ services:
# it falls back to it. # 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}}" 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:-}" CALENDAR_ENCRYPTION_KEY: "${CALENDAR_ENCRYPTION_KEY:-}"
volumes:
- user-files:/app/user-files
healthcheck: healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"] test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"]
interval: 10s interval: 10s
@@ -88,3 +90,4 @@ networks:
volumes: volumes:
pgdata: pgdata:
user-files:
+3
View File
@@ -57,6 +57,8 @@ services:
# it falls back to it. # 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}}" 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:-}" CALENDAR_ENCRYPTION_KEY: "${CALENDAR_ENCRYPTION_KEY:-}"
volumes:
- user-files:/app/user-files
healthcheck: healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"] test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"]
interval: 10s interval: 10s
@@ -91,3 +93,4 @@ networks:
volumes: volumes:
pgdata: pgdata:
user-files:
+30 -14
View File
@@ -270,24 +270,40 @@ Restores stattfinden.
- **PostgreSQL-Daten**: liegen im benannten Docker-Volume `pgdata` - **PostgreSQL-Daten**: liegen im benannten Docker-Volume `pgdata`
(`docker-compose.prod.yml`, Mount `pgdata:/var/lib/postgresql/data` im (`docker-compose.prod.yml`, Mount `pgdata:/var/lib/postgresql/data` im
`db`-Container). Dieses Volume ist der einzige daürhafte Datenspeicher, der über `db`-Container). `pgdata` ist eines von zwei dauerhaften Docker-Volumes (siehe
ein `docker volume`-Backup zusätzlich gesichert werden könnte. 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 - **Verschlüsselungsschlüssel** `TESSERA_ENCRYPTION_KEY`: liegt nur in `.env` auf
dem Host, **nicht** im Datenbank-Dump. Getrennt sichern (siehe Kapitel 2/3) – ohne dem Host, **nicht** im Datenbank-Dump. Getrennt sichern (siehe Kapitel 2/3) – ohne
ihn sind alle per `pg_dump` gesicherten verschlüsselten Zugangsdaten wertlos. ihn sind alle per `pg_dump` gesicherten verschlüsselten Zugangsdaten wertlos.
- **Hochgeladene Dateien** (Avatare unter `user-files/avatars/`, generierte - **Hochgeladene Dateien** (Avatare unter `user-files/avatars/`, generierte
DKV-Exporte unter `user-files/`, siehe `apps/api/src/user/user.controller.ts` und 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 `apps/api/src/dkv/dkv-export.service.ts`): Diese Dateien liegen im benannten
`user-files/` **innerhalb** des `api`-Containers. Weder `docker-compose.yml` noch Docker-Volume `user-files`, gemountet auf `/app/user-files` im Dienst `api`. Der
`docker-compose.prod.yml` mounten dafür ein Docker-Volume oder ein Host-Verzeichnis Mount ist in `docker-compose.yml` und `docker-compose.prod.yml` eingetragen:
– der Ordner existiert ausschließlich in der beschreibbaren Container-Schicht.
**Das bedeutet: Bei jedem `--force-recreate` von `api` gehen Avatare und DKV-Exporte ```yaml
verloren**, sofern auf dem Server nicht zusätzlich (außerhalb des Repo-Standes) # beim Dienst api:
ein Bind-Mount ergänzt wurde. Vor einem Redeploy prüfen, ob auf dem Server ein - user-files:/app/user-files
solcher Mount existiert (`docker inspect tessera-api-1` → `Mounts`); falls # im Top-Level-Block volumes:
nicht, gelten Avatare/Exporte als nicht persistent und sollten bei Bedarf vorher user-files:
manuell aus dem Container kopiert werden (`docker compose cp api:/app/user-files ```
./user-files-backup`).
Damit überstehen Avatare und DKV-Exporte ein `--force-recreate` von `api`. Wie bei
`pgdata` zeigt `docker volume ls` das Volume mit vorangestelltem Projektnamen an
(`<projekt>_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 ## 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. | | 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. | | 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. | | 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 ## 8. Abgrenzung zur CI/CD-Pipeline