Der in 22cd427 ergänzte Bind-Mount ./pb_migrations:/pb/pb_migrations überdeckt das Verzeichnis aus dem Image. Auf einem Deployment-Host, neben dessen compose-Datei kein ausgechecktes Repo liegt, legt Docker dort ein leeres Verzeichnis an: PocketBase findet keine Migration und startet mit einer Datenbank ohne Collections. Genau das ist auf der neu deployten Instanz passiert — users war da (legt PocketBase selbst an), events, teams, riders, runs und times fehlten. Reproduziert und beide Richtungen verifiziert: mit leerem Mount 404 auf allen fünf Collections, ohne Mount kommen alle sechs aus dem Image. Die Migrationen kommen damit wieder ausschließlich über COPY ins Image. Der Preis ist der Weg zurück: Im Admin-UI erzeugte Migrationen liegen nur im Container und müssen mit "docker compose cp" ins Repo geholt werden. Das steht jetzt in backend/README.md und CLAUDE.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
106 lines
4.4 KiB
Markdown
106 lines
4.4 KiB
Markdown
# stammtisch-hersbruck.de — Backend (PocketBase)
|
|
|
|
PocketBase-Instanz für Events, Läufe, Fahrer, Zeiten und Teams.
|
|
|
|
## Lokal starten
|
|
|
|
`.env` aus der Vorlage anlegen (die `.env` selbst steht in `.gitignore`):
|
|
|
|
cp .env.example .env
|
|
|
|
und die Werte eintragen — `.env.example` beschreibt jede Variable. Dann:
|
|
|
|
docker compose up -d --build
|
|
|
|
Admin-UI: http://127.0.0.1:8090/_/ — Login mit `SUPERUSER_EMAIL`/`SUPERUSER_PASSWORD`.
|
|
|
|
Damit das Frontend gegen diese Instanz läuft, in `../frontend/.env`
|
|
`PUBLIC_PB_URL=http://127.0.0.1:8090` setzen.
|
|
|
|
## Schema
|
|
|
|
Das Schema liegt als versionierte Migration in `pb_migrations/` und wird beim
|
|
Start automatisch angewendet. Es enthält die sechs fachlichen Collections
|
|
`users`, `teams`, `events`, `runs`, `riders`, `times`.
|
|
|
|
`1754400000_init_schema.js` ist ein Snapshot der produktiven Instanz. Er wurde
|
|
per `GET /api/collections` abgezogen und ruft `importCollections` mit
|
|
`deleteMissing = false` auf — die Migration legt an und aktualisiert, löscht
|
|
aber nichts, was nicht im Snapshot steht.
|
|
|
|
### Schema-Änderungen aus dem Admin-UI holen
|
|
|
|
`pb_migrations/` wird **nicht** in den Container gemountet — die Migrationen
|
|
kommen über `COPY` ins Image (siehe `Dockerfile`). Das ist Absicht: Ein
|
|
Bind-Mount überdeckt das Image-Verzeichnis, und auf einem Deployment-Host ohne
|
|
ausgechecktes Repo legt Docker dort ein leeres Verzeichnis an. PocketBase
|
|
findet dann keine Migration und startet mit einer Datenbank ohne Collections.
|
|
|
|
Die Kehrseite: Eine im Admin-UI erzeugte Migration liegt zunächst nur im
|
|
Container und muss von Hand herausgeholt werden:
|
|
|
|
# Welche Dateien sind dazugekommen?
|
|
docker compose exec pocketbase ls /pb/pb_migrations
|
|
|
|
# Die neue Datei ins Repo kopieren und committen
|
|
docker compose cp pocketbase:/pb/pb_migrations/<datei> ./pb_migrations/
|
|
|
|
Ohne diesen Schritt ist die Änderung beim nächsten `--build` verloren.
|
|
|
|
Ein frisch gestarteter Container hat damit dasselbe Schema wie die produktive
|
|
Instanz — aber **keine** Daten.
|
|
|
|
## Datenpersistenz
|
|
|
|
Sämtliche Laufzeitdaten liegen in der SQLite-DB unter `/pb/pb_data`: Datensätze,
|
|
Uploads, die SMTP-Einstellungen und der Superuser. Ohne gemountetes Volume auf
|
|
diesem Pfad sind sie nach jedem Redeploy verloren.
|
|
|
|
Das Volume-Mapping `./pb_data:/pb/pb_data` steht in der `docker-compose.yaml`,
|
|
damit die Persistenz-Konfiguration versioniert im Repo liegt.
|
|
|
|
**Trügerisches Signal:** Die Collections kommen aus der Migration und sind nach
|
|
einem Redeploy auch dann da, wenn gar kein Volume existiert. Sie taugen **nicht**
|
|
als Nachweis funktionierender Persistenz — das zeigt nur ein Datensatz, der in
|
|
keiner Migration steht.
|
|
|
|
Mount prüfen:
|
|
|
|
docker inspect <container> --format '{{json .Mounts}}' | jq
|
|
|
|
Erscheint dort kein Eintrag mit `"Destination": "/pb/pb_data"`, fehlt die Persistenz.
|
|
|
|
`pb_data/` steht in `.gitignore` und gehört dort auch hin.
|
|
|
|
## Superuser-Verhalten
|
|
|
|
`entrypoint.sh` ruft `superuser create` auf — bewusst **nicht** `upsert`.
|
|
Existiert der Account bereits, scheitert `create` mit „Value must be unique" und
|
|
das Passwort bleibt unangetastet. Eine im Admin-UI vorgenommene
|
|
Passwortänderung überlebt damit jeden Redeploy.
|
|
|
|
| Situation | Verhalten |
|
|
|---|---|
|
|
| Erster Start, Variablen gesetzt | Superuser wird angelegt |
|
|
| Neustart, Account existiert | „Superuser existiert bereits — Passwort bleibt unverändert" |
|
|
| Passwort im UI geändert, dann Neustart | UI-Passwort gilt weiter, Env wird ignoriert |
|
|
| Variablen nicht gesetzt | Übersprungen, PocketBase gibt Installer-Link im Log aus |
|
|
| Passwort zu kurz (< 8 Zeichen) | Container bricht mit Exitcode 1 ab |
|
|
|
|
Passwort später ändern: **im Admin-UI**, nicht über die Env — eine Änderung der
|
|
Env-Variable hat keine Wirkung mehr, sobald der Account existiert.
|
|
|
|
## PocketBase aktualisieren
|
|
|
|
Die Version ist über das Build-Arg `PB_VERSION` gepinnt (Dockerfile und
|
|
`docker-compose.yaml`). Wert hochsetzen, committen, neu bauen.
|
|
|
|
Ein `latest` gibt es bewusst nicht: Der GitHub-Asset heißt
|
|
`pocketbase_<version>_linux_amd64.zip`, enthält die Version also im Dateinamen.
|
|
Der Pin ist auch gewollt — sonst zieht jeder Rebuild unangekündigt eine neue
|
|
Version, inklusive möglicher DB-Schema-Migrationen.
|
|
|
|
Vor einem Update: Changelog prüfen
|
|
(https://github.com/pocketbase/pocketbase/releases) und `pb_data` sichern —
|
|
PocketBase migriert die DB beim Start automatisch, ein Downgrade ist danach
|
|
nicht mehr ohne Weiteres möglich.
|