Schritt-für-Schritt-Deployment-Anleitung für neuen Server

Neues DEPLOYMENT.md: Voraussetzungen, Repository holen, .env.local
konfigurieren, docker compose up --build, Admin-Zugangsdaten aus
ZUGANGSDATEN.md abholen, Reverse-Proxy/HTTPS (Caddy-Beispiel), Updates,
Backup der Docker-Volumes. Aus README.md verlinkt.

.env.example: LOCAL_PG_PASSWORD war bisher nirgends dokumentiert,
obwohl docker-compose.yml die DATABASE_URL des App-Containers daraus
zusammenbaut (die dortige DATABASE_URL-Zeile wird beim
Docker-Compose-Betrieb ignoriert) – beim Schreiben der Anleitung
aufgefallen und ergänzt.

README.md: RSS-Feed und Vorschläge-Badge in der Funktionstabelle
ergänzt.
This commit is contained in:
2026-07-18 12:23:46 +02:00
parent 6f78762e27
commit 65bfd931cc
3 changed files with 181 additions and 4 deletions
+165
View File
@@ -0,0 +1,165 @@
# Deployment auf einem neuen Server
Schritt-für-Schritt-Anleitung, um Canteen Menu auf einem frischen
(Linux-)Server produktiv zu betreiben. Die App läuft vollständig selbst
gehostet (PostgreSQL + eigene Auth), einzige optionale externe
Abhängigkeit ist DeepL für automatische Übersetzungen.
## Voraussetzungen
- Ein Server (VM oder Root-Server) mit Docker und Docker Compose v2
(`docker compose version` sollte funktionieren; auf den meisten
aktuellen Distributionen per `apt install docker.io
docker-compose-v2` bzw. dem [offiziellen Docker-Install-Skript](https://docs.docker.com/engine/install/)
installierbar).
- Eine Domain, deren DNS-A/AAAA-Eintrag auf die Server-IP zeigt (z. B.
`menu.example.de`). Ohne eigene Domain funktioniert die App auch
unter der reinen Server-IP, dann aber ohne gültiges HTTPS-Zertifikat
und ohne funktionierende Passkeys (siehe unten).
- Mindestens 1 GB RAM frei (App + PostgreSQL sind genügsam, für ~250
Mitarbeiter reicht die kleinste übliche VM-Größe).
## 1. Repository auf den Server holen
```bash
git clone https://github.com/<dein-fork-oder-repo>/canteen-menu.git
cd canteen-menu
```
(Oder per `git pull`/`scp`, falls kein direkter Git-Zugriff vom Server
aus gewünscht ist.)
## 2. Konfiguration anlegen
```bash
cp .env.example .env.local
```
`.env.local` mit einem Editor öffnen und ausfüllen:
| Variable | Was rein muss |
| --- | --- |
| `LOCAL_PG_PASSWORD` | Ein neues, zufälliges Passwort (z. B. `openssl rand -base64 24`) – wird beim ersten Start zum Datenbank-Passwort des mitgelieferten PostgreSQL-Containers |
| `APP_URL` | Die öffentliche URL mit Schema, z. B. `https://menu.example.de` (auch wenn HTTPS erst in Schritt 5 eingerichtet wird – schon jetzt korrekt eintragen, da daraus u. a. die Passkey-Domain abgeleitet wird) |
| `SITE_TITLE` | Optional, Titel im Browser-Tab/Überschrift ohne Logo |
| `ADMIN_EMAIL` | Optional, E-Mail des automatisch angelegten Admin-Erstnutzers. Ohne Angabe: `admin@<Hostname aus APP_URL>` |
| `DEEPL_API_KEY` | Optional, nur für automatische Übersetzung |
`DATABASE_URL` in `.env.local` **nicht** anpassen – die wird nur für
den Betrieb ohne Docker gebraucht (siehe README) und beim
Docker-Compose-Start ignoriert.
Zusätzlich eine leere Datei für die automatisch generierten
Admin-Zugangsdaten anlegen, da sie per Bind-Mount in den Container
eingebunden wird:
```bash
touch ZUGANGSDATEN.md
```
## 3. App starten
```bash
docker compose --env-file .env.local up -d --build
```
Das baut das Docker-Image, startet PostgreSQL und die App, wendet beim
ersten Start automatisch das Datenbankschema an und legt – da die
Datenbank leer ist – einen einzigen Admin-Benutzer mit zufällig
generiertem Passwort an.
Kurz prüfen, ob beides läuft:
```bash
docker compose ps
docker compose logs app --tail 30
```
In den Logs sollte u. a. `bootstrap-admin – Admin angelegt: ...`
stehen.
## 4. Admin-Zugangsdaten abholen
```bash
cat ZUGANGSDATEN.md
```
Enthält E-Mail-Adresse und Passwort des automatisch angelegten
Admin-Kontos. Direkt nach dem ersten Login unter „Mein Konto“ das
Passwort ändern und optional einen Passkey registrieren.
Die App ist jetzt unter `http://<server-ip>:3000` erreichbar – für den
produktiven Betrieb fehlt noch HTTPS/die Domain (nächster Schritt).
## 5. Reverse-Proxy + HTTPS einrichten
Die App selbst spricht nur HTTP auf Port 3000; TLS/HTTPS übernimmt ein
Reverse-Proxy davor. Am einfachsten mit [Caddy](https://caddyserver.com/)
(automatisches Let’s-Encrypt-Zertifikat, keine manuelle Zertifikats-
verwaltung nötig):
```bash
sudo apt install caddy # oder: siehe caddyserver.com/docs/install
```
`/etc/caddy/Caddyfile`:
```
menu.example.de {
reverse_proxy localhost:3000
}
```
```bash
sudo systemctl reload caddy
```
Caddy holt beim ersten Aufruf automatisch ein Zertifikat und leitet
danach HTTP→HTTPS um. Alternative: nginx + certbot, falls bereits ein
nginx-Setup existiert (Standard-`proxy_pass`-Konfiguration auf
`http://127.0.0.1:3000`, Zertifikat separat per `certbot --nginx`).
Danach in `.env.local` sicherstellen, dass `APP_URL=https://menu.example.de`
gesetzt ist (Schema `https`, keine Portangabe), und die App neu
starten, falls diese Variable nach Schritt 2 noch geändert wurde:
```bash
docker compose --env-file .env.local up -d
```
## 6. Danach
- Passkeys funktionieren erst mit korrekt gesetzter `APP_URL` (RP-Domain
wird direkt daraus abgeleitet) und über HTTPS (Passkeys verlangen eine
sichere Origin, `localhost` ausgenommen).
- Weitere Kantinenmitarbeiter/Admins unter „Benutzer“ (nur als Admin
sichtbar) anlegen.
- Essenszeiten, Gerichte, Allergene/Zusatzstoffe sind zu Beginn leer und
werden im Kantinen-Bereich gepflegt.
- Logo/Favicon/Farben unter „Design“ (nur Admin) anpassen.
## Updates einspielen
```bash
git pull
docker compose --env-file .env.local up -d --build
```
Neue Datenbank-Migrationen (`db/migrations/*.sql`) werden beim Start
automatisch und idempotent angewendet, bestehende Daten bleiben
erhalten.
## Backup
Zwei Docker-Volumes enthalten alle persistenten Daten:
- `canteen-menu_pgdata` – die komplette PostgreSQL-Datenbank
- `canteen-menu_branding_data` – hochgeladenes Logo/Favicon
```bash
docker run --rm -v canteen-menu_pgdata:/data -v "$PWD":/backup alpine \
tar czf /backup/pgdata-backup.tar.gz -C /data .
```
Für ein reines Datenbank-Backup eignet sich auch `docker compose exec db
pg_dump -U postgres canteen_menu > backup.sql`.