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> |
||
|---|---|---|
| .. | ||
| pb_hooks | ||
| pb_migrations | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yaml | ||
| Dockerfile | ||
| entrypoint.sh | ||
| README.md | ||
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.