# GerbilManager — Betriebsanleitung (TrueNAS SCALE Goldeye) > Zielgruppe: Julian (Systemadministration) und Ehefrau (tägliche Nutzung). > Bookmark für die Ehefrau: **http://\/** (z. B. http://truenas/) --- ## Inhaltsverzeichnis 1. [Übersicht & Architektur](#1-übersicht--architektur) 2. [Voraussetzungen](#2-voraussetzungen) 3. [Erstinstallation auf TrueNAS Goldeye](#3-erstinstallation-auf-truenas-goldeye) 4. [App starten / stoppen / aktualisieren](#4-app-starten--stoppen--aktualisieren) 5. [Backup & Wiederherstellung](#5-backup--wiederherstellung) 6. [ZFS-Snapshot-Schichtung](#6-zfs-snapshot-schichtung) 7. [CI/CD via Gitea Actions](#7-cicd-via-gitea-actions) 8. [Fehlerbehebung](#8-fehlerbehebung) --- ## 1. Übersicht & Architektur ``` Browser / Handy | HTTP :80 (oder PORT aus .env, z.B. 8080) v ┌──────────────────┐ │ frontend (nginx) │ statisches React-SPA + Reverse-Proxy └──────┬───────────┘ │ /api/* → http://api:8080/* │ /scalar → http://api:8080/scalar v ┌──────────────────┐ │ api (.NET 10) │ GerbilManagerWebAPI, Minimal API └──────┬───────────┘ │ ConnectionStrings__gerbilmanager v ┌──────────────────┐ ┌──────────────────────┐ │ db (Postgres 17)│ │ backup (Sidecar) │ └──────────────────┘ │ pg_dump + tar + cron │ │ └──────────────────────┘ └─ pgdata-Volume → /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata photos-Volume → /mnt/JailStorage/DockerVolumes/gerbilmanager/photos backups-Volume → /mnt/JailStorage/DockerVolumes/gerbilmanager/backups keys-Volume → /mnt/JailStorage/DockerVolumes/gerbilmanager/keys ``` **Einziger veröffentlichter Port:** `80` (konfigurierbar via `PORT` in `.env`). Alles andere läuft intern im Docker-Netz. --- ## 2. Voraussetzungen | Was | Details | |-----|---------| | TrueNAS SCALE | **25.10.2.1 „Goldeye"** (native Docker Custom Apps) | | Container Registry | `git.rismer.de/gulum` (externes HTTPS) | | Docker | bereits auf TrueNAS Goldeye vorhanden | | Verzeichnisse | 4 Ordner unter `/mnt/JailStorage/DockerVolumes/gerbilmanager/` anlegen (Schritt 3.1) | --- ## 3. Erstinstallation auf TrueNAS Goldeye ### 3.1 Verzeichnisse anlegen und Berechtigungen setzen Öffne eine Shell auf der NAS (TrueNAS → System → Shell oder SSH): ```bash # Vier Ordner anlegen mkdir -p /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata mkdir -p /mnt/JailStorage/DockerVolumes/gerbilmanager/photos mkdir -p /mnt/JailStorage/DockerVolumes/gerbilmanager/backups mkdir -p /mnt/JailStorage/DockerVolumes/gerbilmanager/keys # Postgres-Container läuft als UID 999 (postgres) / GID 999 intern. # pgdata muss von UID 999 beschreibbar sein; postgres erzwingt chmod 0700. chown -R 999:999 /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata chmod 700 /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata # photos, backups und keys werden von der API bzw. dem Sidecar beschrieben # (laufen als root im Container) — keine weiteren ACL-Anpassungen nötig. ``` > **TrueNAS Dataset-ACL-Hinweis:** Falls `JailStorage` ein ZFS-Dataset mit NFSv4-ACLs ist, > und `chown` meldet „Operation not permitted": setze in TrueNAS → Datasets → > `JailStorage` → Berechtigungen → **ACL-Typ: POSIX** (oder nutze das UI-Formular > „Eigentümer: 999, Gruppe: 999" für das `pgdata`-Unterverzeichnis). ### 3.2 Registry-Login auf der NAS ```bash docker login git.rismer.de # Benutzername und Token/Passwort eingeben (Gitea-Account oder Access Token mit read:packages) ``` Der Login wird unter `/root/.docker/config.json` gespeichert und bleibt nach Reboots erhalten. ### 3.3 Repository klonen ```bash git clone https://git.rismer.de/gulum/GerbilManager.git /opt/gerbilmanager cd /opt/gerbilmanager ``` ### 3.4 Konfiguration anlegen ```bash cp deploy/truenas/.env.example deploy/truenas/.env nano deploy/truenas/.env ``` Mindestens setzen: | Variable | Wert | |----------|------| | `POSTGRES_PASSWORD` | Sicheres Passwort (mind. 20 Zeichen, keine `"`) | | `AI__BaseUrl` | Gemini: `https://generativelanguage.googleapis.com/v1beta/openai` | | `AI__ApiKey` | Dein Gemini API-Key | | `AI__Model` | `gemini-2.0-flash` (oder `gemini-flash-latest`) | | `PORT` | `80` — falls Port 80 auf der NAS bereits belegt ist: **auf `8080` ändern** | Die Pfad-Variablen (`PGDATA_PATH`, `PHOTOS_PATH`, etc.) sind bereits auf die Goldeye-Standardpfade vorbelegt und müssen nur geändert werden, wenn du einen anderen Pool nutzt. ### 3.5 Images ziehen und App starten ```bash cd /opt/gerbilmanager docker compose -f deploy/truenas/compose.yaml pull docker compose -f deploy/truenas/compose.yaml up -d ``` Erster Start dauert ca. 2–3 Minuten (Postgres-Init + EF-Migrationen). > **TrueNAS Goldeye Custom App (Alternative):** > Statt der Shell kann die App auch über TrueNAS → Apps → „Custom App installieren" → > „Install via YAML" deployt werden: compose-Inhalt einfügen, Volumes als Host-Pfade > konfigurieren. Die Shell-Methode ist einfacher und gibt mehr Kontrolle. ### 3.6 Verifikation ```bash # Alle 4 Container laufen? docker compose -f deploy/truenas/compose.yaml ps # API-Healthcheck (erwartet: {"status":"Healthy"}) curl -s http://localhost/api/health # Tier-Gesamtanzahl prüfen (erwartet > 0 nach Import) curl -s "http://localhost/api/gerbils?pageSize=1" | grep -o '"totalCount":[0-9]*' # API-Doku (Scalar) im Browser http:///scalar # Foto-Upload: in der Webapp ein Tier öffnen → Foto hochladen → Foto erscheint ``` --- ## 4. App starten / stoppen / aktualisieren ### Starten ```bash cd /opt/gerbilmanager docker compose -f deploy/truenas/compose.yaml up -d ``` ### Stoppen ```bash docker compose -f deploy/truenas/compose.yaml down ``` ### Aktualisieren (nach CI-Push auf main) ```bash cd /opt/gerbilmanager git pull docker compose -f deploy/truenas/compose.yaml pull docker compose -f deploy/truenas/compose.yaml up -d ``` > EF-Migrationen laufen automatisch beim API-Start — kein manueller Schritt nötig. --- ## 5. Backup & Wiederherstellung ### Automatisches Backup Der `backup`-Sidecar-Container läuft dauerhaft und sichert täglich um **03:00 Uhr**: - Kompletter `pg_dump` der Datenbank als `.sql` - Komprimiertes Foto-Archiv als `.tar.gz` - Rotation: Backups älter als `BACKUP_KEEP_DAYS` (Standard: 7) werden gelöscht Backups liegen unter: `/mnt/JailStorage/DockerVolumes/gerbilmanager/backups/YYYY-MM-DD_HH-MM/` ``` /mnt/JailStorage/DockerVolumes/gerbilmanager/backups/ 2026-06-06_03-00/ gerbilmanager_2026-06-06_03-00.sql (Datenbank-Dump, Klartext SQL) photos_2026-06-06_03-00.tar.gz (Fotos) backup.log (Protokoll) ``` ### Manuelles Backup auslösen ```bash docker compose -f deploy/truenas/compose.yaml exec backup /bin/sh /scripts/backup.sh ``` ### Backup-Log prüfen ```bash tail -50 /mnt/JailStorage/DockerVolumes/gerbilmanager/backups/backup.log ``` Backup-Validierung: Das Skript prüft ob der Dump `CREATE TABLE` enthält — fehlt dieser Marker, erscheint eine WARNUNG im Log. Größe 0 KB bedeutet Fehlschlag. ### Wiederherstellung — Runbook > **WARNUNG:** Alle aktuellen Datenbankdaten und Fotos werden überschrieben! **Schritt 1:** API und Frontend stoppen (DB und backup-Sidecar laufen weiter) ```bash docker compose -f deploy/truenas/compose.yaml stop api frontend ``` **Schritt 2:** Restore ausführen ```bash # Neuestes Backup automatisch wählen und bestätigen: docker compose -f deploy/truenas/compose.yaml exec -T backup \ /bin/sh /scripts/restore.sh latest -f # Bestimmtes Backup (Datum aus Verzeichnisname): docker compose -f deploy/truenas/compose.yaml exec -T backup \ /bin/sh /scripts/restore.sh 2026-06-06_03-00 -f ``` Das Skript: 1. Trennt alle offenen DB-Verbindungen 2. Spielt den SQL-Dump mit `psql -h db -U postgres -d gerbilmanager < dump.sql` ein 3. Entpackt das Foto-Archiv nach `/data/photos` **Schritt 3:** API neu starten ```bash docker compose -f deploy/truenas/compose.yaml start api frontend ``` **Schritt 4 — Verifikation (Pflicht nach erstem Restore-Drill):** ```bash # Tier-Anzahl prüfen curl -s "http://localhost/api/gerbils?pageSize=1" | grep -o '"totalCount":[0-9]*' # ColorVariety-Anzahl (Stammdaten, erwartet: >= 60) curl -s http://localhost/api/color-varieties | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" # Foto stichprobenartig prüfen ls /mnt/JailStorage/DockerVolumes/gerbilmanager/photos/ | head -5 ``` ### Restore-Nachweis (Round-Trip-Test, lokal 2026-06-06) Protokoll vom getesteten Restore auf lokalem Aspire-Postgres: ``` 73 ColorVarieties vorhanden → DELETE 12 Zeilen → 61 verbleibend → psql < dump.sql eingespielt → 73 ColorVarieties bestätigt Exit-Code: 0 ``` **Erster TrueNAS-Restore-Drill:** nach Erstinstallation bitte ausführen und Tier-Anzahl notieren — beweist dass Backup + Restore auf dem NAS korrekt funktionieren. --- ## 6. ZFS-Snapshot-Schichtung ZFS-Snapshots ergänzen die pg_dump-Backups als zweite Sicherungsebene. Sie schützen vor versehentlichem Datenverlust auf Dataset-Ebene. ### Empfohlene Snapshot-Konfiguration In TrueNAS → **Datasets** → `JailStorage/DockerVolumes/gerbilmanager` → **Snapshots** → **Regelmäßige Snapshots**: | Unterordner | Häufigkeit | Aufbewahrung | |-------------|-----------|--------------| | `.../photos` | Stündlich | 24 Stunden | | `.../photos` | Täglich | 30 Tage | | `.../pgdata` | Stündlich | 24 Stunden | | `.../pgdata` | Täglich | 30 Tage | | `.../backups` | Täglich | 90 Tage | > **Hinweis:** `pgdata` enthält Live-Postgres-Dateien. ZFS-Snapshots davon sind crash-konsistent, > aber **nicht** application-konsistent — für einen sauberen DB-Restore immer den `pg_dump` verwenden, > nicht den ZFS-Snapshot von `pgdata`. ### Snapshot manuell erstellen (z. B. vor Update) ```bash # Pool-/Dataset-Name anpassen falls nötig zfs snapshot JailStorage/DockerVolumes/gerbilmanager/photos@vor-update-$(date +%Y%m%d) zfs snapshot JailStorage/DockerVolumes/gerbilmanager/backups@vor-update-$(date +%Y%m%d) ``` ### Aus ZFS-Snapshot wiederherstellen (Fotos) ```bash # Snapshots auflisten zfs list -t snapshot JailStorage/DockerVolumes/gerbilmanager/photos # Einzelne Datei aus Snapshot kopieren cp /mnt/JailStorage/DockerVolumes/gerbilmanager/photos/.zfs/snapshot//datei.jpg \ /mnt/JailStorage/DockerVolumes/gerbilmanager/photos/ ``` --- ## 7. CI/CD via Gitea Actions CI pusht Images nach Erfolg zu `git.rismer.de/gulum/gerbilmanager-api` und `git.rismer.de/gulum/gerbilmanager-frontend`. ### Registry-Secrets in Gitea Gitea → Repository → Einstellungen → Secrets: | Secret | Wert | |--------|------| | `REGISTRY_USER` | Gitea-Benutzername | | `REGISTRY_TOKEN` | Gitea Access Token mit `package:write` | ### Update nach CI-Push ```bash # Auf der NAS nach erfolgreichem CI-Lauf: cd /opt/gerbilmanager git pull docker compose -f deploy/truenas/compose.yaml pull docker compose -f deploy/truenas/compose.yaml up -d ``` --- ## 8. Fehlerbehebung ### App startet nicht ```bash docker compose -f deploy/truenas/compose.yaml logs docker compose -f deploy/truenas/compose.yaml logs api docker compose -f deploy/truenas/compose.yaml logs db ``` ### Port 80 belegt Falls Port 80 vom TrueNAS-System selbst genutzt wird: ```bash # In deploy/truenas/.env: PORT=8080 # Dann neu starten: docker compose -f deploy/truenas/compose.yaml up -d ``` ### Postgres startet nicht (Permission denied auf pgdata) ```bash # UID 999 muss Eigentümer des pgdata-Verzeichnisses sein: chown -R 999:999 /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata chmod 700 /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata docker compose -f deploy/truenas/compose.yaml restart db ``` ### Registry-Pull schlägt fehl ```bash # Neu einloggen: docker login git.rismer.de # Dann pull wiederholen: docker compose -f deploy/truenas/compose.yaml pull ``` ### Datenbank nicht erreichbar ```bash docker compose -f deploy/truenas/compose.yaml ps db docker compose -f deploy/truenas/compose.yaml exec db \ psql -U postgres -d gerbilmanager -c "\dt" ``` ### Backup-Fehler ```bash tail -50 /mnt/JailStorage/DockerVolumes/gerbilmanager/backups/backup.log docker compose -f deploy/truenas/compose.yaml exec backup /bin/sh /scripts/backup.sh ``` ### Fotos werden nicht angezeigt ```bash docker compose -f deploy/truenas/compose.yaml exec api ls /data/photos ``` ### Kompletter Neustart (Daten bleiben erhalten) ```bash docker compose -f deploy/truenas/compose.yaml down docker compose -f deploy/truenas/compose.yaml up -d ```