Die App
Self-Hosting
Compose-Anleitungen, das erste Konto, Betrieb ohne Docker, erster Start, HTTPS, Backups, Upgrades
Diese Seite wurde maschinell aus der englischen Dokumentation übersetzt.
openplate wird als vorgefertigtes Multi-Arch-Docker-Image (linux/amd64 + linux/arm64; Rechner der Raspberry-Pi-Klasse sind ein vorrangiges Ziel) ausgeliefert und in der GitHub Container Registry veröffentlicht. Der Tag latest ist die neueste Version. Jede Version hat zudem ihren eigenen Versions-Tag. Der Tag main folgt jeder Änderung auf dem Main-Branch. Es müssen keine Geheimnisse erzeugt werden, und außer deinem eigenen Schlüssel für einen KI-Anbieter musst du dich nirgends registrieren.
Hier gibt es keine abgespeckte Version. Lokales Erfassen, BYOK-Tellererkennung per KI, PWA-Installation, JSON-Export/-Import, die gesamte Oberfläche: identisch mit der gehosteten Instanz, weil es dasselbe Image ist. Das Einzige, was das gehostete Setup hinzufügt, ist der optionale Sync-Dienst, den wir für dich betreiben, und auch der ist quelloffen, damit du ihn selbst betreiben kannst.
Was du ausführen kannst
- Die App allein. Ergänzt einen Container. Du erhältst ein schnelles Setup ohne Datenbank oder Secrets, die du verwalten musst. Du riskierst den Verlust deines Tagebuchs, wenn du den Browser leerst, es gibt keinen geräteübergreifenden Sync und Scans erfordern einen Cloud-KI-Schlüssel.
- Die App plus der Core-Server. Ergänzt
openplate-coreund Postgres. Du erhältst verschlüsselten Sync über Geräte hinweg und eine optionale gemeinsame KI-Abrechnung. Du riskierst Datenverlust, wenn du die Datenbank, das Secret und deinen Wiederherstellungsschlüssel nicht sicherst. - Die App samt selbst gehosteter Inferenz. Ergänzt
openplate-inference. Du erhältst lokale Teller-Scans ohne Cloud-Konto und ohne dass Fotos dein Netzwerk verlassen. Du riskierst Hardware-Belastung, zudem muss jeder Browser den Inferenz-Container direkt erreichen. - Alles. Sync und Inferenz zusammen, insgesamt vier Container. Du erhältst vollständigen Datenschutz bei geräteübergreifendem Sync. Du riskierst den höchsten Betriebsaufwand und die stärkste Ressourcenlast.
Jede Ausprägung entspricht einer Compose-Datei unter docker/topologies/; topologies.md erklärt die Auswahl.
Bevor du beginnst
Installiere Docker. Ein neuer Server hat es nicht. Folge der Docker-Anleitung für deine Distribution unter docs.docker.com/engine/install. Unter Ubuntu 24.04 funktionieren auch die Distributionspakete:
sudo apt-get update
sudo apt install docker.io docker-compose-v2
sudo usermod -aG docker "$USER" # then log out and back in, to use docker without sudoPodman funktioniert ebenfalls. Lies podman.md für die Unterschiede.
Wähle einen dauerhaften Ordner. Jede Anleitung unten beginnt mit mkdir -p ~/openplate && cd ~/openplate. Die Compose-Datei und ihre .env liegen dort. Führe jeden späteren Befehl von dort aus, einschließlich Upgrades, Backups und Logs. Verwende nicht /tmp. Ein Neustart kann das Verzeichnis leeren, und deine .env mit ihren Secrets verschwindet.
Wenn du dies für deine Familie betreibst, erledige zwei Dinge frühzeitig. - Sichere.envund die Datenbank. Kopiere.envan einen sicheren Ort, vor allem die ZeileSERVER_SECRET. Ohne sie öffnet eine wiederhergestellte Datenbank kein Konto. Sichere die Datenbank danach nach Zeitplan. Backups enthält die Befehle. - Lass andere Geräte nur den HTTPS-Port erreichen. Viele Server starten ohne Firewall, sodass jeder von einem Container veröffentlichte Port in deinem Netzwerk offen ist. Belasse die Container-Ports auf127.0.0.1, wie im Abschnitt HTTPS gezeigt, damit nur dein Reverse-Proxy sie erreicht. Eine Firewall wieufwschließt sie nicht für dich, da Docker seine Ports daran vorbei veröffentlicht.ufwschließt dennoch alles andere auf dem Server. Erlaube zuerst SSH, dann HTTPS: ``bash sudo ufw allow OpenSSH sudo ufw allow 443/tcp sudo ufw allow 8443/tcp # the core server's HTTPS port, in the recipes below sudo ufw enable`With a domain name, Caddy also needs port 80 for its certificate:sudo ufw allow 80/tcp`.
Die App allein
mkdir -p ~/openplate && cd ~/openplate
curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/compose.yml
docker compose -f compose.yml up -dÖffne es alshttp://localhost:3000auf dem Server selbst oder über HTTPS. Von einem anderen Gerät aus zeigthttp://<the server's address>:3000das Tagebuch an. Die Installation der App, die Offline-Nutzung und die Ein-Klick-Verbindung zu OpenRouter funktionieren dort nicht. Siehe HTTPS.
Podman führt diese Dateien mit podman compose aus. Unter Ubuntu benötigt dieser Unterbefehl das installierte Paket podman-compose als Anbieter: siehe podman.md.
Du erhältst einen Container, keine Datenbank, keinen .env-Schritt und kein Secret, das du generieren musst. Die App ist unter http://localhost:3000 erreichbar.
Der Port wird auf der Schnittstelle jeder veröffentlicht. Die App ist unter der LAN-Adresse dieses Rechners erreichbar, sobald up -d zurückkehrt. Ändere die Zeile ports: in einem geteilten Netzwerk zu '127.0.0.1:3000:3000' und erreiche sie stattdessen über einen Reverse-Proxy.
TRUST_PROXY legt fest, wie viele Reverse-Proxys vor der App stehen. Die Compose-Datei verwendet standardmäßig 1. Das ist hinter einem Proxy wie Caddy oder nginx korrekt. Ohne dies sieht die CSRF-Prüfung der App die falsche Adresse und Formularübertragungen schlagen fehl. Setze es ohne vorgeschalteten Proxy auf 0:
echo "TRUST_PROXY=0" >> .env
docker compose -f compose.yml up -dOhne Proxy funktionieren Seiten bei beiden Werten. Allerdings erlaubt 1 einem Besucher, seine Adresse in X-Forwarded-For zu fälschen und das Limit für Lebensmittelabfragen pro Adresse zu umgehen.
Baue das Image selbst
Um aus dem Quellcode zu bauen, statt das veröffentlichte Image herunterzuladen, kommentiere image: in docker/compose.yml aus, hebe die Auskommentierung von build: auf und führe dies vom Root-Verzeichnis des Repositories aus, da der Build-Kontext relativ zu dieser Datei ist:
docker compose --project-directory . -f docker/compose.yml build
docker compose --project-directory . -f docker/compose.yml up -dFür den Betrieb ganz ohne Container siehe Ohne Docker.
Jedes andere Setup (Sync, selbst gehostete Inferenz oder beides) ist eine separate Datei unter docker/topologies/. Siehe topologies.md, um eines auszuwählen.
Die App plus dein eigener Core-Server
docker/topologies/compose.core.yml ist die Referenz-Bereitstellung für die App, den Core-Server und die Postgres-Datenbank, die sync benötigt. Die App verbindet sich weiterhin mit keiner eigenen Datenbank. (Für Self-Hosting der Inferenz siehe Die App plus Sync und selbst gehostete Inferenz unten. Dieses Setup nutzt dieselbe Abgleich-Konfiguration, ergänzt um die Modell-Runtime.)
mkdir -p ~/openplate && cd ~/openplate
curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/topologies/compose.core.yml
# The core server needs exactly one secret. Generate it and keep it with your backups.
echo "SERVER_SECRET=$(openssl rand -hex 32)" >> .env
# Your key to the admin API. You need it to create the first account.
echo "ADMIN_TOKEN=$(openssl rand -hex 32)" >> .env
# The URLs a BROWSER will use to reach each service. Skip these two for a test
# on this machine, or through the ssh tunnel in the HTTPS section.
echo "PUBLIC_APP_URL=https://openplate.example.com" >> .env
echo "PUBLIC_SYNC_URL=https://sync.example.com" >> .env
# 1 behind one reverse proxy, 0 with none.
echo "TRUST_PROXY=1" >> .env
docker compose -f compose.core.yml up -dmkdir -p ~/openplate && cd ~/openplate
curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/topologies/compose.core.yml
echo "SERVER_SECRET=$(openssl rand -hex 32)" >> .env
echo "ADMIN_TOKEN=$(openssl rand -hex 32)" >> .env
echo "PUBLIC_APP_URL=https://openplate.example.com" >> .env
echo "PUBLIC_SYNC_URL=https://sync.example.com" >> .env
echo "TRUST_PROXY=1" >> .env
podman compose -f compose.core.yml up -dKonten erfordern eine sichere Seite. Anmeldung, Registrierung und das Öffnen eines Einladungslinks schlagen über reineshttp://<the server's address>fehl: Der Browser blockiert die Kryptografie, die sie nutzen. Liefere beide Adressen über HTTPS aus oder teste überlocalhost. Siehe HTTPS.
Lies README von openplate-core, bevor du diese letzte Zeile auf einem Rechner ausführst, den andere erreichen können. Beide Dienste veröffentlichen ihre Ports auf jeder Schnittstelle. Der Kontodienst ist ab dem Moment des Starts erreichbar, und der Betrieb eines Kontodienstes ist ein größeres Unterfangen als der Betrieb der App.
Die Datei ist Zeile für Zeile kommentiert, einschließlich der beiden Einstellungen, die bei falscher Konfiguration Probleme verursachen (SERVER_SECRET und TRUST_PROXY). TRUST_PROXY gilt für beide Dienste, da sie hinter demselben Proxy oder hinter gar keinem liegen. Die Datei übergibt jede Variable, die die Dienste aus .env lesen, an deren Container. environment-variables.md listet sie alle auf. Unter sync.md findest du Erklärungen dazu, was Sync ist und wie der Client ihn erreicht.
Das erste Konto erstellen
Niemand kann sich selbst registrieren. Ein Konto entsteht durch das Öffnen einer Einladung, die an eine E-Mail-Adresse gerichtet ist. Die erste Einladung für dich selbst erzeugst du auf dem Server mit ADMIN_TOKEN. Führe dies in ~/openplate mit deiner eigenen Adresse aus. Port 3001 ist der Port, auf dem compose.core.yml und compose.full.yml den Core-Server veröffentlichen. Die eigene Compose-Datei des Core-Servers in apps/core nutzt stattdessen Port 3000:
ADMIN_TOKEN=$(grep '^ADMIN_TOKEN=' .env | cut -d= -f2)
curl -s -X POST http://127.0.0.1:3001/v1/admin/invites \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","displayName":"You","role":"admin"}'Die Antwort ist eine Zeile JSON. Der relevante Teil sieht so aus:
"emailed":false,"link":"https://openplate.example.com/join#server=https%3A%2F%2Fsync.example.com&invite=si_..."- Kein Mailversand konfiguriert (Standard):
"emailed": false. Es wurde niemand angeschrieben. Kopiere dielinkund öffne sie selbst. - Mailversand konfiguriert (siehe E-Mail):
"emailed": true. Derselbe Link ist auch per E-Mail an diese Adresse unterwegs.
Öffne den Link im Browser über eine sichere Seite, wähle ein Passwort, und das Konto existiert. Ein Smartphone oder zweites Gerät kann sich erst anmelden, nachdem du HTTPS eingerichtet hast, da der SSH-Tunnel und localhost nur einen einzelnen Computer bedienen. Der Link funktioniert einmal und läuft nach sieben Tagen ab. "role":"admin" macht dieses erste Konto zum Administrator. Von da an lädst du Personen direkt in der App unter /admin ein. Auf einer Instanz ohne E-Mail zeigt sie jeden neuen Link an. Für ein normales Mitglied lässt du role weg. E-Mail erklärt beide Wege: jeden Link manuell weiterzugeben oder den Core-Server den Versand per E-Mail übernehmen zu lassen.
Füge auf einer verwalteten Instanz, auf der der Core-Server für die Scans aller Nutzer zahlt, "dailyAiLimit":200 in den Body ein, um dem Konto 200 KI-Anfragen pro Tag zu gewähren. Der Standardwert ist 0. Eine verwaltete Instanz benötigt vier weitere Zeilen in .env. Scans starten ohne Modell nicht, da die App kein Modell auf deine Rechnung auswählt:
echo "INSTANCE_MODE=managed" >> .env
echo "UPSTREAM_BASE_URL=https://openrouter.ai/api/v1" >> .env
echo "UPSTREAM_API_KEY=sk-or-..." >> .env
echo "AI_ADVERTISED_MODEL=vendor/model-name" >> .envErsetze vendor/model-name durch das Modell deines Anbieters, genau so geschrieben, wie der Anbieter es schreibt. Statt AI_ADVERTISED_MODEL kannst du AI_TIERS_FILE=bundled setzen, dann übernimmt der Core-Server das Modell aus seiner Tier-Datei, ai-tiers.json. Siehe configuration.md.
Wenn jemand das Passwort vergisst
Wenn Mailversand konfiguriert ist, versendet Passwort vergessen in der App einen Link zum Zurücksetzen, und das Tagebuch ist nach dem Zurücksetzen wieder da. Ohne Mailversand kann die App nichts senden, und die Seite zum Zurücksetzen weist den Benutzer an, den Administrator zu fragen. Du erstellst den Link so:
- In der App: Verwaltung, öffne unter Personen die Person und wähle Link zum Zurücksetzen senden. Ohne Mailversand zeigt die Seite den Link an. Teile ihn so, wie du auch ein Passwort teilen würdest.
- Auf dem Server: ermittle die
iddes Kontos und fordere dann einen Link dafür an. Der Port ist wieder 3001, wie in den Compose-Dateien der Topologie.
ADMIN_TOKEN=$(grep '^ADMIN_TOKEN=' .env | cut -d= -f2)
curl -s http://127.0.0.1:3001/v1/admin/accounts -H "Authorization: Bearer $ADMIN_TOKEN"
curl -s -X POST http://127.0.0.1:3001/v1/admin/accounts/1/reset-mail \
-H "Authorization: Bearer $ADMIN_TOKEN"Der zweite Aufruf antwortet mit {"emailed":false,"link":"https://openplate.example.com/reset#server=...&token=sr_..."}. Ein Link zum Zurücksetzen funktioniert einmal und läuft nach einer Stunde ab.
Der Core-Server kann Einladungs- und Passwort-Reset-Mails senden. Er muss es nicht. Eine Familien-Instanz läuft ganz ohne E-Mail-Setup, und das ist der einfachste Weg.
Keine E-Mail
Lass alle E-Mail-Einstellungen leer. Der Core-Server verschickt keine Mails. Er zeigt dir stattdessen jeden Link an, und du gibst ihn wie ein Passwort weiter.
- Eine Einladung: öffne Verwaltung unter
/adminund wähle Jemanden einladen. Gib die Adresse ein und wähle Einladung senden. Die Seite meldet Einladung bereit für diese Adresse und zeigt den Link. Wähle Link kopieren und sende ihn an die Person, zum Beispiel in einer privaten Nachricht. Wer den Link besitzt, kann das Konto eröffnen. - Ein vergessenes Passwort: öffne unter Personen die Person und wähle Link zum Zurücksetzen senden. Die Seite zeigt den Link. Gib ihn auf demselben Weg weiter. Er funktioniert einmal, innerhalb einer Stunde.
- Eine verlorene Einladung: wähle unter Einladungen neben der Adresse Erneut senden. Dadurch wird ein neuer Link erzeugt und der alte ungültig. Die Seite zeigt den neuen Link zum Kopieren an. Wähle Zurück zur Liste, um zu deinen offenen Einladungen zurückzukehren.
Verwendet der Link eine andere Adresse als dein Browser, erscheint darunter eine Warnung. Setze PUBLIC_APP_URL und PUBLIC_SYNC_URL auf die Adressen, die deine Familie nutzt, und erzeuge den Link erneut. Die Seite warnt dich außerdem, wenn der Link die Seite zwar öffnet, die App aber zu einem Core-Server unter localhost oder einer einfachen http://-Adresse leitet, die andere Geräte nicht erreichen können. Setze PUBLIC_SYNC_URL auf die https://-Adresse, die deine Familie verwendet, und erzeuge den Link erneut.
OPEN_SIGNUP=true erlaubt Fremden, ein Konto anzufragen. Ohne konfigurierte E-Mail verweigert der Core-Server mit dieser Einstellung den Start. Eine Familien-Instanz lässt sie deaktiviert, und sie bleibt aus, bis du sie setzt. Registrierung mit Turnstile erklärt die Einstellung und ihr Captcha.
SMTP
Jedes Standard-E-Mail-Konto kann die E-Mails über SMTP versenden. Füge diese Zeilen zu .env hinzu:
SMTP_HOST: der Servername, ohne Schema und ohne Port.SMTP_PORT:587, wenn du es weglässt.SMTP_USERundSMTP_PASSWORD: die Zugangsdaten. Setze beide oder lass beide leer für einen Server, der keine Anmeldung erfordert.SMTP_FROM: die Absenderadresse, als reine Adresse oderName <address>.MAIL_OPERATOR_EMAIL: deine eigene Adresse. Sie empfängt deine Kopie einer Kündigung oder eines Widerrufs. Beide Transportwege erfordern sie.
Der Port entscheidet über den Verschlüsselungsmodus. Port 465 nutzt TLS von Beginn an. Jeder andere Port muss über STARTTLS hochstufen, und der Dienst sendet keine E-Mails an einen Server, dem dies fehlt. Klartext ist nur erlaubt, wenn SMTP_HOST eine Loopback-Adresse wie localhost ist, für einen lokalen Fänger wie Mailpit (siehe Mit Mailpit testen). Der Dienst prüft Zertifikate immer.
Ein Gmail-Konto benötigt ein App-Passwort. Google erzeugt dieses nur für Konten mit aktivierter Bestätigung in zwei Schritten. Googles SMTP-Einstellungen gib smtp.gmail.com und Port 587 an:
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=family.openplate@gmail.com
SMTP_PASSWORD="the app password"
SMTP_FROM="openplate <family.openplate@gmail.com>"
MAIL_OPERATOR_EMAIL=you@example.orgAmazon SES benötigt Für SES erstellte SMTP-Zugangsdaten, die sich von deinem AWS-Zugriffsschlüssel unterscheiden, sowie eine verifizierte Absendeadresse. Der Host nennt deine AWS-Region. Einzelheiten findest du in der Endpunktliste. Solange dein Konto in der SES-Sandbox bleibt, stellt SES nur an verifizierte Empfängeradressen zu.
SMTP_HOST=email-smtp.eu-central-1.amazonaws.com
SMTP_PORT=587
SMTP_USER=<SES SMTP user name>
SMTP_PASSWORD=<SES SMTP password>
SMTP_FROM="openplate <noreply@example.org>"
MAIL_OPERATOR_EMAIL=you@example.orgErstelle den Core-Server nach jeder Änderung an .env mit docker compose -f <your file> up -d neu.
Eine HTTP-Mail-API
Ein E-Mail-Dienst mit HTTP-API funktioniert ebenso. Setze alle drei Einstellungen MAIL_API_URL, MAIL_API_KEY und MAIL_API_FROM, zusätzlich MAIL_OPERATOR_EMAIL. Der Core-Server sendet jede Mail als JSON-POST-Anfrage an MAIL_API_URL, mit MAIL_API_KEY als Bearer-Token, im von der Resend-API erwarteten Format. Resend ist ein kompatibler Dienst. Bei Resend ist MAIL_API_URL gleich https://api.resend.com/emails.
Konfiguriere nur einen Übertragungsweg. Wenn du sowohl SMTP als auch die E-Mail-API setzt, verweigert der Core-Server den Start.
Mail benötigt die öffentlichen Adressen
Jede Mail enthält einen Link, und dieser Link muss sich auf dem Smartphone des Empfängers öffnen lassen. Wenn du SMTP oder eine E-Mail-API konfigurierst, setze PUBLIC_APP_URL und PUBLIC_SYNC_URL auf die https://-Adressen, die deine Familie verwendet. Verwendet eine der Einstellungen einfaches http:// oder eine Loopback-Adresse wie localhost, verweigert der Core-Server den Start. Sein Protokoll nennt jeden zu korrigierenden Wert in einer Nachricht, die so beginnt:
Mail is configured. Its messages would carry links that recipients cannot open.Die Protokollmeldung bezeichnet diese Werte als CLIENT_BASE_URL und SERVER_PUBLIC_URL. Das sind die internen Namen, die der Core-Server liest, und die Compose-Dateien bilden sie aus PUBLIC_APP_URL und PUBLIC_SYNC_URL ab. Sieh dir die Meldung mit docker compose -f <your file> logs core an.
Mail-Funktion prüfen
Sende in /admin eine Einladung an eine zweite eigene Adresse. Die Seite sollte Einladung gesendet an für diese Adresse anzeigen und die E-Mail sollte in deinem Posteingang ankommen. Zeigt die Seite Einladung bereit für an und gibt einen Link aus, ist die Zustellung fehlgeschlagen. Der Link bleibt gültig. Prüfe das Core-Server-Protokoll auf eine Zeile mit Mail send failed, um den Grund zu sehen. Wenn du mit dem Testen fertig bist, wähle unter Einladungen für die Testeinladung Zurückziehen.
Mit Mailpit testen
Mailpit fängt jede Mail ab und zeigt sie auf einer Webseite an, sodass du E-Mails ohne E-Mail-Konto testen kannst. Führe es als Sidecar aus, der das Netzwerk des Core-Containers teilt. Der Core-Server erreicht ihn dann unter localhost, wo Klartext erlaubt ist. Speichere diese Datei neben deiner Compose-Datei als compose.mailpit.yml:
# compose.mailpit.yml: a mail catcher for testing, next to your compose file
services:
core:
ports:
- '127.0.0.1:8025:8025' # Mailpit's web page, on this machine only
mailpit:
image: docker.io/axllent/mailpit:latest
restart: unless-stopped
network_mode: 'service:core'Füge diese Zeilen zu .env hinzu. Mailpit nimmt E-Mails auf Port 1025 entgegen:
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_FROM="openplate <test@example.org>"
MAIL_OPERATOR_EMAIL=you@example.orgStarte beide Dateien zusammen, sende dann eine Einladung und lies sie unter http://localhost:8025 auf dem Server:
docker compose -f compose.core.yml -f compose.mailpit.yml up -dDie Regel in Mail benötigt die öffentlichen Adressen gilt weiterhin. Setze zuerst PUBLIC_APP_URL und PUBLIC_SYNC_URL auf https://-Adressen, sonst startet der Core-Server nicht.
Ein separater Mailpit-Container, der über den Dienstnamen wie etwa SMTP_HOST=mailpit angesprochen wird, funktioniert nicht. Der Core-Server sendet Klartext nur an diese Maschine. Ein Dienstname gilt als anderer Host. Der Dienst verlangt STARTTLS, Mailpit bietet keins, und jede Mail schlägt mit Mail send failed im Protokoll fehl. Das geteilte Netzwerk platziert Mailpit auf derselben Maschine.
Wenn du mit dem Testen fertig bist, entferne die vier Zeilen aus .env. Entferne Mailpit danach mit docker compose -f compose.core.yml up -d --remove-orphans.
Ein Relay mit einer privaten Zertifizierungsstelle
Der Core-Server prüft das Zertifikat jedes E-Mail-Servers. Ein Relay in einem Firmennetzwerk nutzt möglicherweise ein Zertifikat, das von einer privaten Zertifizierungsstelle signiert ist. Node.js vertraut dieser Zertifizierungsstelle standardmäßig nicht. Übergib dem Core-Server das Zertifikat dieser Zertifizierungsstelle als PEM-Datei. Mounte die Datei in den Container und setze NODE_EXTRA_CA_CERTS auf ihren Pfad. Zum Beispiel in einer compose.ca.yml neben deiner Compose-Datei:
# compose.ca.yml: trust a private certificate authority for the mail relay
services:
core:
volumes:
- ./relay-ca.pem:/etc/openplate/relay-ca.pem:roecho "NODE_EXTRA_CA_CERTS=/etc/openplate/relay-ca.pem" >> .env
docker compose -f compose.core.yml -f compose.ca.yml up -dNode.js liest die Datei einmalig beim Start. Das Zertifikat wird zu denen hinzugefügt, denen Node.js bereits vertraut, sodass öffentliche E-Mail-Server weiterhin funktionieren.
Die App plus Self-Hosting-Inferenz
docker/topologies/compose.inference.yml führt die App neben openplate-inference aus. Tellerfotos werden auf deiner eigenen Hardware ausgewertet, und jeder Besucher erhält per Fingertipp den Hinweis „Dieses openplate stellt seine eigene KI bereit“. Lies zuerst den Hardware-Abschnitt von topologies.md. Das kleine Modell lite benötigt auf einer CPU etwa 1,6 GB RAM und wenige Sekunden bis eine Minute pro Teller.
mkdir -p ~/openplate && cd ~/openplate
curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/topologies/compose.inference.yml
# One key, generated here on the server. The inference service accepts it and
# the app hands it to every browser.
echo "INFERENCE_API_KEY=opk_$(openssl rand -hex 24)" >> .env
# The two URLs a BROWSER will use. Replace 192.168.1.20 with this machine's
# address, or with the names your reverse proxy serves.
echo "PUBLIC_APP_URL=http://192.168.1.20:3000" >> .env
echo "PUBLIC_INFERENCE_URL=http://192.168.1.20:8300/v1" >> .env
# 0 with no reverse proxy, 1 behind one.
echo "TRUST_PROXY=0" >> .env
docker compose -f compose.inference.yml up -d
docker compose -f compose.inference.yml logs -f inferenceEinfaches HTTP genügt für Scans, HTTPS erfordert durchgehend HTTPS. Über einfacheshttp://<the server's address>erreicht ein Tellerfoto den Inferenz-Container, aber die Installation der App funktioniert nicht. Sobald die App überhttps://läuft, muss auch die Inferenz-Adressehttps://sein. Andernfalls blockiert der Browser den Aufruf von der sicheren Seite. Siehe HTTPS.
PUBLIC_INFERENCE_URL muss eine Adresse sein, die ein Browser öffnen kann, da das Foto vom Smartphone direkt an den Inferenz-Container gesendet wird. http://inference:8300/v1, der Name, den die Container untereinander verwenden, funktioniert dort nicht. Behalte das /v1 am Ende bei.
Der erste Start lädt etwa 2 GiB an Gewichten (1,96 GiB) in ein benanntes Volume herunter, was in unseren Tests sechs bis sieben Minuten dauerte, und lädt anschließend das Modell. Das Protokoll zeigt jeden Schritt an. Die App ist sofort einsatzbereit; die Ein-Klick-KI funktioniert, sobald dies mit 200 antwortet:
curl -s http://127.0.0.1:8300/readyzDer Schlüssel steht im Quelltext der Seite, die jeder Browser lädt, sodass ihn jeder lesen kann, der die App öffnen kann. In einem Heimnetzwerk oder Tailnet ist das in Ordnung, auf einer öffentlich über das Internet erreichbaren Instanz jedoch falsch. configuration.md enthält die vollständige Regel. Der Inferenz-Container belegt bis auf zwei alle CPU-Kerne; passe LLAMA_THREADS in .env an, um das zu ändern.
Die App plus Sync und selbst gehostete Inferenz
docker/topologies/compose.full.yml betreibt vier Container: die App, den Core-Server, Postgres und die Inferenz per Self-Hosting. Lies zuerst Die App plus dein eigener Core-Server und Die App plus Self-Hosting-Inferenz. Dieser Abschnitt behandelt nur, was sich ändert, wenn alle Teile zusammen laufen.
mkdir -p ~/openplate && cd ~/openplate
curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/topologies/compose.full.yml
# The core server needs exactly one secret. Generate it and keep it with your backups.
echo "SERVER_SECRET=$(openssl rand -hex 32)" >> .env
# Your key to the admin API. You need it to create the first account.
echo "ADMIN_TOKEN=$(openssl rand -hex 32)" >> .env
# One key for the inference service, which the app hands to every browser.
echo "INFERENCE_API_KEY=opk_$(openssl rand -hex 24)" >> .env
# The URLs a BROWSER will use to reach each service. PUBLIC_APP_URL and
# PUBLIC_SYNC_URL default to localhost, so skip both for a test on this
# machine. PUBLIC_INFERENCE_URL has no such default: set it even for a
# local test, for example to http://localhost:8300/v1.
echo "PUBLIC_APP_URL=https://openplate.example.com" >> .env
echo "PUBLIC_SYNC_URL=https://sync.example.com" >> .env
echo "PUBLIC_INFERENCE_URL=https://ai.example.com/v1" >> .env
# 1 behind one reverse proxy, 0 with none.
echo "TRUST_PROXY=1" >> .env
docker compose -f compose.full.yml up -dErstelle das erste Konto genauso wie unter oben beschrieben. Beim ersten Start werden auch die Inferenz-Gewichte heruntergeladen, etwa 2 GiB. Das Protokoll zum Modell-Laden und die Bereitschaftsprüfung funktionieren wie unter Die App plus Self-Hosting-Inferenz. Für echte Geräte statt eines Testbetriebs solltest du alle drei Adressen hinter HTTPS betreiben. Siehe HTTPS.
Ohne Docker
Die App ist ein einzelnes Node.js-Programm. Sie läuft direkt aus einem Checkout. So betreibst du sie auf Ubuntu 24.04 mit systemd, damit sie über Logouts und Neustarts hinweg aktiv bleibt.
Node.js 24 oder neuer. Ubuntu 24.04 liefert nodejs Version 18 mit, was zu alt ist. Installiere 24 aus NodeSource:
curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs git
node --version # v24.xnvm funktioniert ebenfalls, legt Node aber in deinem Home-Ordner ab. Die systemd-Unit unten muss dann diesen Pfad verwenden (command -v node gibt ihn aus).
pnpm in der vom Repository vorgegebenen Version. corepack wird mit Node 24 mitgeliefert. Es lädt genau das pnpm herunter, das im Feld packageManager der package.json der App hinterlegt ist, aber nur innerhalb von apps/app. Außerhalb dieses Ordners greift pnpm standardmäßig auf das zurück, was corepack wählt. Führe jeden Befehl für pnpm innerhalb von apps/app aus. Bestätige beim ersten Durchlauf die Download-Aufforderung.
sudo corepack enable
git clone https://github.com/LowCarbCheck/openplate.git ~/openplate-src
cd ~/openplate-src/apps/app
pnpm install --frozen-lockfile
pnpm buildDie Einstellungen. Der Server liest .env aus dem Ordner, in dem er ausgeführt wird. In Produktionsumgebungen startet er ohne APP_URL nicht:
cat > .env <<'EOF'
NODE_ENV=production
PORT=3000
APP_URL=http://localhost:3000
TRUST_PROXY=0
EOFSetze APP_URL auf die Adresse, die Personen aufrufen, und TRUST_PROXY=1, sobald ein Reverse-Proxy davor steht. Jede andere Variable von die App gehört in dieselbe Datei. HOST=127.0.0.1 sorgt dafür, dass der Server nur auf dieser Maschine lauscht, was hinter einem Proxy auf derselben Box gewünscht ist.
Eine systemd-Unit, damit die App beim Systemstart anläuft und nach dem Abmelden weiterläuft. Die Werte für $USER und $HOME unten werden beim Einfügen eingesetzt:
sudo tee /etc/systemd/system/openplate.service > /dev/null <<EOF
[Unit]
Description=openplate
After=network-online.target
Wants=network-online.target
[Service]
User=$USER
WorkingDirectory=$HOME/openplate-src/apps/app
Environment=NODE_ENV=production
ExecStart=/usr/bin/node --import tsx ./server.ts
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now openplate
curl -s http://127.0.0.1:3000/healthchecksudo journalctl -u openplate -f zeigt das Protokoll. Ziehe für ein Upgrade den Code erneut, baue das Projekt und starte anschließend neu:
cd ~/openplate-src/apps/app
git pull
pnpm install --frozen-lockfile
pnpm build
sudo systemctl restart openplateDer Abschnitt HTTPS gilt hier unverändert: Leite den Reverse-Proxy auf Port 3000.
Erster Start
- Öffne die App und folge der kurzen Einführung. Keine Registrierung, kein Login: Wer die App auf einem Gerät öffnet, ist der Nutzer dieses Geräts.
- Gehe zu Einstellungen → KI und verbinde einen KI-Anbieter über deinen eigenen API-Schlüssel (OpenRouter, Mistral, einen eigenen OpenAI-kompatiblen Endpunkt oder Anthropic). Unter configuration.md findest du den Klick-Ablauf für OpenRouter und die Anleitung, wie du stattdessen einen instanzeigenen Endpunkt bereitstellst.
- Erstelle frühzeitig ein Backup: Einstellungen → Daten & Backup → Alles herunterladen (JSON). Dein Tagebuch liegt im Speicher dieses Browsers, daher ist ein Backup die einzige Kopie, die das Löschen der Websitedaten oder einen Wechsel auf ein neues Gerät übersteht.
- Wenn mehr als eine Person auf dieser Instanz scannt, erstelle einen kostenlosen Lebensmitteldatenbank-Schlüssel unter lowcarbcheck.org/developers, trage ihn in
.envalsFOOD_DB_API_KEY=...ein und führedocker compose -f <your file> up -derneut aus. Ohne Schlüssel teilen sich alle Nutzer der Instanz ein einziges kleines anonymes Kontingent. Siehe configuration.md. Mit einem Schlüssel kannst du auchFOOD_DB_BACKFILL=trueaktivieren, wodurch die von Nutzern aus einer KI-Antwort übernommenen Lebensmittel als Vorschläge an LowCarbCheck gesendet werden. Siehe configuration.md.
HTTPS
Browser beschränken etliche Funktionen auf einen sicheren Kontext. Ein sicherer Kontext ist eine Seite, die über https:// oder über localhost auf der lokalen Maschine bereitgestellt wird. openplate setzt für mehrere Kernfunktionen einen sicheren Kontext voraus:
- Konten, Anmeldung und Sync. Registrierung, Anmeldung sowie das Öffnen von Einladungs- oder Wiederherstellungs-Links leiten Schlüssel über die Web Crypto API (
crypto.subtle) ab. Browser schalten diese API auf einfachenhttp://-Seiten ab. Unterhttp://192.168.1.20:3000schlagen diese Ansichten fehl. Auch das Teilen und die Forschungskonsole funktionieren dann nicht, da sie auf dieselbe API zurückgreifen. - Mit OpenRouter verbinden, die 1-Klick-Anmeldung bei OpenRouter, aus demselben Grund. Das manuelle Einfügen eines Schlüssels funktioniert immer.
- Installation der App und Offline-Nutzung (der Service-Worker).
Über einfaches HTTP von einem anderen Gerät aus bleiben das Tagebuch, manuelle Einträge, Datensicherungen und Tellerfotos weiterhin nutzbar. Die Foto-Schaltfläche übergibt die Aufnahme über einen Dateidialog an die Kamera des Smartphones, was keine sichere Verbindung verlangt. Die Schnellstart-Anleitung funktioniert auf dem Server selbst ohne Änderungen, da localhost als sicher gilt.
Deine Geräte benötigen eine sichere Adresse. Du kannst eine auf vier Arten einrichten: ein SSH-Tunnel für einen schnellen Test, Caddy mit einem Domainnamen, Caddy in einem Heimnetzwerk ohne Domainnamen oder Tailscale.
Ein schneller Test von einem Computer aus: ein SSH-Tunnel
Dies ist ein Test für einen einzelnen Computer, kein Setup. Der Tunnel bedient nur den Computer, auf dem er läuft, und nur während der Befehl ausgeführt wird. Ein Telefon oder ein zweites Gerät kann sich darüber nicht anmelden. Richte für diese HTTPS mit Caddy weiter unten ein.
Um Konten und Abgleich vor der Zertifikatskonfiguration zu testen, leite die beiden Ports an deinen Computer weiter. Lass PUBLIC_APP_URL und PUBLIC_SYNC_URL ungesetzt, damit beide ihre Standardwerte localhost behalten. Lass E-Mail hier ebenfalls ungesetzt. Ist E-Mail konfiguriert, verweigert der Core-Server den Start, solange seine Link-Adressen localhost lauten. Ohne E-Mail startet er, und du kopierst jeden Link selbst. Führe diesen Befehl auf deinem Rechner aus, nicht auf dem Server:
ssh -N -L 3000:localhost:3000 -L 3001:localhost:3001 you@192.168.1.20Öffne http://localhost:3000 in deinem Browser, während der Befehl läuft. Dies gilt als sichere Seite, die Anmeldung funktioniert also. Ein Einladungslink vom Server (http://localhost:3000/join#...) öffnet sich dort ebenfalls. Füge -L 8300:localhost:8300 für den Inferenz-Container hinzu.
Ein Domainname: Caddy
Caddy ruft ein Let's-Encrypt-Zertifikat automatisch ab und erneuert es. Es erfordert einen Domainnamen, der auf deinen Server verweist. Außerdem müssen die Ports 80 und 443 für die Zertifikatsprüfung aus dem Internet erreichbar sein.
# Caddyfile
openplate.example.com {
reverse_proxy localhost:3000
}Setze als Nächstes APP_URL=https://openplate.example.com in .env. Setze TRUST_PROXY=1, was der Produktionsstandard ist. Erstelle den Dienst app mit docker compose -f compose.yml up -d neu. Ein einfaches docker compose restart liest .env nicht neu ein. Es startet nur den vorhandenen Container neu, sodass neue Werte nie geladen werden.
Gib dem Core-Server für den Abgleich einen eigenen Domänennamen. Setze beide öffentlichen URLs anstelle von APP_URL:
# Caddyfile
openplate.example.com {
reverse_proxy localhost:3000
}
sync.example.com {
reverse_proxy localhost:3001
}PUBLIC_APP_URL=https://openplate.example.com
PUBLIC_SYNC_URL=https://sync.example.com
TRUST_PROXY=1Ändere die Zeile ports: in '127.0.0.1:3000:3000', und verwende '127.0.0.1:3001:3000' für den Sync. Wende die Änderung mit docker compose -f <your file> up -d an. Ein Reverse Proxy hebt die Veröffentlichung von Container-Ports nicht auf. Wenn du es bei '3000:3000' belässt, liefert die App neben der HTTPS-Adresse weiterhin einfaches HTTP auf Port 3000 über dein lokales Netzwerk aus.
Podman erstellt den Dienst mit podman compose -f compose.yml up -d auf dieselbe Weise neu. Beachte ein Rootless-Detail, bevor du den Reverse Proxy weglässt: Ein Rootless-Podman-Container kann ohne zusätzliche Konfiguration keinen Host-Port unter 1024 binden. Die direkte Veröffentlichung auf Port 80 oder 443 erfordert zuerst sudo sysctl net.ipv4.ip_unprivileged_port_start=80. Siehe podman.md.
Kein Domainname, nur Heimnetzwerk: Caddy mit lokalem Zertifikat
Ohne Domainnamen kann Caddy dennoch HTTPS in deinem Heimnetzwerk bereitstellen. Es erstellt eine eigene Zertifizierungsstelle und signiert damit ein Zertifikat für die Adresse des Servers. Jedes Telefon und jeder Computer, der openplate öffnet, muss dieser Stelle einmalig vertrauen. Danach erhalten die Telefone der Familie eine sichere Seite. Anmeldung, Sync und die Installation der App funktionieren über https://.
Gib dem Server eine feste Adresse. Reserviere im Router die aktuelle Adresse des Servers, zum Beispiel 192.168.1.20, damit sie sich nie ändert. Das Zertifikat und beide öffentlichen Adressen verweisen darauf.
Installiere Caddy und richte es auf die Adresse aus. Unter Ubuntu installiert sudo apt install caddy Caddy als Dienst. Ersetze /etc/caddy/Caddyfile durch Folgendes und setze dabei die Adresse deines Servers ein. tls internal weist Caddy an, das Zertifikat selbst zu signieren:
# /etc/caddy/Caddyfile
https://192.168.1.20 {
tls internal
reverse_proxy localhost:3000
}
https://192.168.1.20:8443 {
tls internal
reverse_proxy localhost:3001
}sudo systemctl reload caddySetze dieselben Adressen in .env, erstelle die Container dann neu mit docker compose -f <your file> up -d:
PUBLIC_APP_URL=https://192.168.1.20
PUBLIC_SYNC_URL=https://192.168.1.20:8443
TRUST_PROXY=1Für die App allein setze stattdessen APP_URL=https://192.168.1.20. Lass den zweiten Block aus dem Caddyfile weg. Ändere wie im Rezept oben die ports:-Zeilen zu '127.0.0.1:3000:3000' und '127.0.0.1:3001:3000', damit nur Caddy die Container erreicht.
Kopiere Caddys Root-Zertifikat vom Server herunter. Caddys Ubuntu-Paket speichert es unter /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt. Das Zertifikat ist öffentlich. Sein privater Schlüssel liegt im selben Ordner und darf den Server niemals verlassen, kopiere also nur root.crt:
sudo cp /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt ~/openplate-root.crt
sudo chown "$USER" ~/openplate-root.crtHole es auf deinen Computer mit scp you@192.168.1.20:openplate-root.crt .. Schicke es dann an jedes Smartphone, zum Beispiel als E-Mail-Anhang an dich selbst oder per AirDrop.
Vertraue ihm einmalig auf jedem Gerät. Das ist der Schritt, der dafür sorgt, dass die Smartphones der Familie über https:// funktionieren.
- iPhone und iPad: Öffne die Datei und erlaube den Download. Tippe in Einstellungen oben auf Profil geladen oder suche es unter Allgemein > VPN und Geräteverwaltung, und installiere es. Aktiviere anschließend das volle Vertrauen dafür unter Einstellungen > Allgemein > Info > Zertifikatsvertrauenseinstellungen. Ohne diesen letzten Schritt verweigert der Browser weiterhin die Seite.
- Android: Speichere die Datei auf dem Smartphone. Öffne Einstellungen > Sicherheit > Verschlüsselung & Anmeldedaten > Zertifikat installieren > CA-Zertifikat, bestätige die Warnung und wähle die Datei aus. Auf neueren Geräten beginnt der Pfad bei Sicherheit und Datenschutz > Weitere Sicherheitseinstellungen, und die Bezeichnungen unterscheiden sich je nach Hersteller etwas. Wenn das Smartphone keine Bildschirmsperre hat, fordert Android dich auf, eine einzurichten.
- Ein Computer: Füge es zu den Zertifikaten des Systems hinzu. Manche Browser führen eine eigene Liste und brauchen es auch dort.
Öffne https://192.168.1.20 auf einem Smartphone. Die Seite sollte ohne Warnung laden, und die Anmeldung sollte funktionieren. Die Adresse funktioniert nur in deinem Heimnetzwerk. Sichere Caddys Datenordner in deinen Backups. Eine Neuinstallation von Caddy erstellt eine neue Zertifizierungsstelle, und jedes Gerät muss der neuen dann erneut vertrauen.
Jeder andere Reverse-Proxy
nginx, Traefik oder ein anderer Proxy kann Caddy ersetzen. Wir haben keinen davon getestet, daher ist dies eine Checkliste, keine Anleitung. Der Proxy muss all dies leisten:
- Zwei
https://-Adressen. Die App und der Core-Server erhalten jeweils eine eigene. - Dieselben Adressen in
.env. SetzePUBLIC_APP_URLundPUBLIC_SYNC_URLgenau auf diese Adressen. Für die App allein ist dasAPP_URL. TRUST_PROXY=1, oder die Anzahl der Proxys in der Kette.HostundX-Forwarded-Protoerreichen die App. Reiche denHost-Header des Browsers unverändert durch, oder setzeX-Forwarded-Hostdarauf. SetzeX-Forwarded-Protoaufhttps. Die CSRF-Prüfung der App baut daraus die eigene Adresse der Seite und vergleicht sie mit demOrigindes Browsers. Stimmen sie nicht, schlagen Formularübertragungen fehl.X-Forwarded-Forerreicht beide Dienste. Deren Limits pro Adresse lesen sie aus.- Die Container-Ports bleiben auf
127.0.0.1. Behalte'127.0.0.1:3000:3000'und'127.0.0.1:3001:3000'bei, damit am Proxy vorbei nichts zu ihnen gelangt.
Kein Domainname: Tailscale Serve
Tailscale gibt jeder Maschine in deinem Tailnet eine HTTPS-Adresse unter ts.net. Du benötigst keinen Domänennamen, keine offenen Ports und keine manuelle Zertifikatsverwaltung. Tailscale Serve schaltet diese Adresse vor einen Port auf dieser Maschine. openplate mit Abgleich benötigt zwei Adressen. Du stellst zwei Ports unter demselben Maschinennamen bereit: die App auf 443 und den Core-Server auf 8443.
Bevor du beginnst:
- MagicDNS und HTTPS-Zertifikate aktivieren für dein Tailnet auf der DNS-Seite der Tailscale-Admin-Konsole. Tailscales HTTPS-Leitfaden enthält die Schritte. Der Rechnername erscheint in einem öffentlichen Zertifikats-Log, wähle also einen Namen, der nichts Privates verrät.
- Jedes Familienmitglied nutzt Tailscale auf jedem Gerät, das openplate öffnet. Jede Person muss in deinem Tailnet sein oder diesen Rechner für sich freigegeben haben.
- Die Container-Ports auf
127.0.0.1belassen:'127.0.0.1:3000:3000'für die App und'127.0.0.1:3001:3000'für Sync. Tailscale Serve erreicht sie auf diesem Rechner. Nichts anderes benötigt Zugriff.
Gib dann beide Ports frei. --bg lässt sie im Hintergrund weiterlaufen, und Tailscale gibt sie nach einem Neustart wieder frei:
tailscale serve --bg --https=443 3000
tailscale serve --bg --https=8443 3001
tailscale serve statusTailscale stellt das Zertifikat aus und erneuert es. Setze beide Adressen in .env mit deinen eigenen Rechner- und Tailnet-Namen:
PUBLIC_APP_URL=https://<machine-name>.<tailnet>.ts.net
PUBLIC_SYNC_URL=https://<machine-name>.<tailnet>.ts.net:8443
TRUST_PROXY=1Tailscale Serve ist ein Reverse-Proxy, TRUST_PROXY bleibt also bei 1. Erstelle den Stack mit docker compose -f <your file> up -d neu. Wenn du die App alleine betreibst, gib nur Port 3000 frei und setze APP_URL auf die erste Adresse.
Diesen Pfad haben wir nicht durchgängig getestet. Die tailscale serve-Referenz dokumentiert die obigen Flags, und Tailscales Dokumentation beschreibt tailscale serve. Die obige Caddy-Anleitung ist diejenige, die wir überprüft haben.
Headscale, ein selbst gehosteter Tailscale-Control-Server, stellt keine HTTPS-Zertifikate aus. Ein Headscale-Tailnet benötigt stattdessen die Caddy-Anleitung mit einer Domain.
Backups
Auf dem App-Server gibt es nichts zu sichern. Er enthält keine Datenbank und schreibt keinen Zustand: Ein zerstörter App-Container verliert nichts.
Der gerätespezifische JSON-Export ist das entscheidende Backup: Einstellungen → Daten & Backup → Alles herunterladen (JSON). Diese Datei ist die Kopie, die einen gelöschten Browser oder ein defektes Telefon übersteht. Die App blendet ein Erinnerungsbanner ein, wenn ein Gerät Daten enthält, die du noch nie oder schon länger nicht exportiert hast.
Behandle den Export genauso vertraulich wie das Tagebuch. Sie enthält jeden Eintrag im Klartext, und sie enthält außerdem den privaten Schlüssel der Freigabe-Identität dieses Geräts sowie die Wurzel, aus der das Forschungs-Pseudonym abgeleitet wird. Wer die Datei besitzt, kann ein Tagebuch öffnen, das für diese Person freigegeben wurde, und die Studienbeiträge zu dieser Person zurückverfolgen. Zwei Dinge bleiben außen vor: der Schlüssel des KI-Anbieters und die Tellerfotos, die das Gerät nie verlassen.
Wenn du auch den Core-Server betreibst, lohnt sich für sein Postgres ein regelmäßiger Dump, zusammen mit dem SERVER_SECRET, der ohne die Datenbank nutzlos ist und umgekehrt:
docker compose -f compose.core.yml exec postgres \
pg_dump -U openplate openplate_sync > sync-backup.sqlAktualisierung
docker compose -f compose.yml pull
docker compose -f compose.yml up -dVerwende dieselbe -f-Datei, mit der du bereitgestellt hast. Wenn du eine Topologie aus docker/topologies/ gestartet hast, gib stattdessen diese Datei an, zum Beispiel docker compose -f compose.core.yml pull. Ein bloßes docker compose pull neben compose.core.yml schlägt mit no configuration file provided: not found fehl.
Die Compose-Dateien verwenden das Tag latest, welches die neueste Version ist, also bringt dich pull dorthin. Um den Zeitpunkt deines Upgrades selbst zu bestimmen, pinne eine Version in der Zeile image:, zum Beispiel ghcr.io/lowcarbcheck/openplate:0.54.0. Ändere die Nummer, wenn du die nächste Version möchtest. Der Core-Server und der Inferenzdienst haben eigene Versionsnummern, pinne also jedes Image auf seine eigene. Das Tag main folgt jeder Änderung auf dem Haupt-Branch. Es ist zum Testen gedacht, nicht für einen Server, den deine Familie nutzt.
Es gibt nichts zu migrieren: Der App-Container speichert keinen Zustand, daher ersetzt ein neues Image einfach das alte. Wenn du den gesamten Stack betreibst, führt der Core-Server seine eigenen Migrationen beim Start aus.
Aktualisierung von einem Image vor 0.1.x (einmalig)
Ältere Images betrieben ein eigenes Kontosystem und ein eigenes Postgres. Beides ist weg. Das Upgrade löscht die Tabelle users und alles, was daran hängt: Konten, Sitzungen, Bestätigungs- und Reset-Tokens. Nichts von dem, was du protokolliert hast, ist betroffen: Die Daten des Trackers waren bereits auf das Gerät gewandert. Die Änderung ist unumkehrbar, also:
- Erstelle zuerst eine Sicherung. Erstelle ein
pg_dumpder alten App-Datenbank, wenn die Kontenzeilen wiederherstellbar bleiben sollen, und lass jede Person auf jedem Gerät einen JSON-Export über Profil → Deine Daten erstellen. Dieser Export ist die Kopie, die ihr Tagebuch enthält. - Führe das Upgrade durch. Aktualisiere zuerst die Zeile
image:aufghcr.io/lowcarbcheck/openplate:latest, das neueste Release: Vor-0.1.x-Images wurden unterghcr.io/sprqvntrs/openplateveröffentlicht, und ein Pull ohne diese Änderung holt bloß das alte Image erneut. Die Migration läuft dann beim Starten des Containers. Danach gibt es keine Anmeldeseite mehr: Jedes Gerät, auf dem bereits Daten liegen, behält sie und hört einfach auf zu fragen, wer du bist. - Bereinige deine
.env. Die Variablen für Session-Secret, Encryption-Key, Signup-Gate und Seeded-Superadmin existieren nicht mehr, ebenso wenig wieDB_HOST,DB_PORT,DB_USER,DB_PASSWORD,DB_NAMEund die Variablen zur Pool-Optimierung. Nichts liest sie mehr aus. Sie gesetzt zu lassen ist harmlos, aber sie sind Ballast. - Altes Volume löschen, sobald du zufrieden bist. Ältere Releases legten keinen Compose-Projektnamen fest, daher stammte das Präfix aus dem Verzeichnis, in dem du gearbeitet hast (prüfe zuerst den tatsächlichen Namen):
docker volume ls | grep pg-data, danndocker compose down && docker volume rm <that name>.
Wenn zwei Konten im selben Browserprofil angemeldet waren, beachte, dass der Gerätespeicher immer gerätebezogen war: Ihre Daten teilten sich bereits einen Speicher und das bleibt so. Getrennte Browserprofile bleiben der Weg, um die Tagebücher zweier Personen auf einem Gerät auseinanderzuhalten.