CP_EHRENAMT/DEPLOY.md
2026-06-25 18:58:44 +02:00

154 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DEPLOY.md — CheckPoint Ehrenamt auf einen EU-VPS
> Ziel: App unter **gunicorn** hinter **nginx**, **HTTPS** per Let's Encrypt,
> Secrets aus der Umgebung, **SQLite + Uploads persistent** in `instance/`,
> tägliches **DB-Backup**. Befehle für die **Fish-Shell**, auf dem VPS auszuführen.
>
> Platzhalter überall ersetzen: Domain `checkpoint.example.org`, ggf. Pfad
> `/opt/checkpoint`. Annahme: frischer Debian/Ubuntu-VPS in der EU, Domain zeigt
> per A/AAAA-Record auf die VPS-IP, du hast einen sudo-fähigen Login.
---
## 1 · Systempakete
```fish
sudo apt update
sudo apt install -y python3 python3-venv python3-pip nginx sqlite3 git certbot python3-certbot-nginx
```
## 2 · Dienst-Nutzer + Verzeichnis
```fish
sudo useradd --system --create-home --home-dir /opt/checkpoint --shell /usr/sbin/nologin checkpoint
sudo mkdir -p /opt/checkpoint
```
## 3 · Code holen
```fish
sudo git clone <DEIN_REPO_URL> /opt/checkpoint
# oder per scp/rsync hochladen. Danach Eigentümer setzen:
sudo chown -R checkpoint:checkpoint /opt/checkpoint
```
## 4 · Virtualenv + Abhängigkeiten
```fish
cd /opt/checkpoint
sudo -u checkpoint python3 -m venv .venv
sudo -u checkpoint .venv/bin/pip install --upgrade pip
sudo -u checkpoint .venv/bin/pip install -r requirements.txt
```
## 5 · Secrets (`.env`)
`.env` liegt unter `/opt/checkpoint/.env`, ist **nicht** im Git und wird von systemd
geladen. Aus der Vorlage erzeugen und ein echtes Secret eintragen:
```fish
cd /opt/checkpoint
sudo -u checkpoint cp .env.example .env
# Secret erzeugen und einsetzen:
set SECRET (sudo -u checkpoint .venv/bin/python -c "import secrets; print(secrets.token_urlsafe(48))")
sudo -u checkpoint sed -i "s|^SECRET_KEY=.*|SECRET_KEY=$SECRET|" .env
sudo chmod 600 .env
```
`.env` sollte enthalten: `CP_ENV=production` und `SECRET_KEY=<lang & zufällig>`.
## 6 · Datenbank anlegen, Team seeden, Admin erstellen
```fish
cd /opt/checkpoint
# DB-Tabellen + erstes Team (CheckPoint) + die zwei Chat-Kanäle (idempotent):
sudo -u checkpoint --preserve-env=CP_ENV env CP_ENV=production .venv/bin/python seed.py
# Ersten Admin anlegen (Nutzername Anzeigename Passwort):
sudo -u checkpoint env CP_ENV=production SECRET_KEY=$SECRET \
.venv/bin/flask --app wsgi create-admin admin "Team-Leitung" "EinStarkesPasswort"
```
> Hinweis: DB (`instance/checkpoint.sqlite`) und Uploads (`instance/uploads/`)
> liegen unter `/opt/checkpoint/instance/` und bleiben über Neustarts/Updates
> erhalten. Nur dieses Verzeichnis ist schreibbar (systemd `ReadWritePaths`).
## 7 · gunicorn als systemd-Dienst
```fish
sudo cp /opt/checkpoint/deploy/checkpoint.service /etc/systemd/system/checkpoint.service
sudo systemctl daemon-reload
sudo systemctl enable --now checkpoint
sudo systemctl status checkpoint --no-pager
```
Test (lokal auf dem VPS): `curl -I http://127.0.0.1:8000` sollte eine Antwort
liefern (Redirect zur Anmeldung ist ok).
## 8 · nginx als Reverse Proxy
```fish
sudo cp /opt/checkpoint/deploy/nginx-checkpoint.conf /etc/nginx/sites-available/checkpoint
sudo sed -i "s/checkpoint.example.org/DEINE-DOMAIN/" /etc/nginx/sites-available/checkpoint
sudo ln -sf /etc/nginx/sites-available/checkpoint /etc/nginx/sites-enabled/checkpoint
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
```
`client_max_body_size 12m` ist gesetzt (passt zum 10-MB-Upload-Limit der App).
Statische Dateien (CSS/JS/Schriften) liefert nginx direkt; Uploads/Dokumente
laufen bewusst **nicht** über nginx, sondern zugriffsgeschützt über die App.
## 9 · HTTPS (Let's Encrypt)
```fish
sudo certbot --nginx -d DEINE-DOMAIN
```
certbot ergänzt den 443-Block, richtet die Weiterleitung 80→443 ein und erneuert
automatisch (Timer `certbot.timer`). Erst **nach** HTTPS greifen die `Secure`-Cookies
sinnvoll — `CP_ENV=production` setzt sie bereits voraus.
## 10 · Tägliches DB-Backup
```fish
sudo chmod +x /opt/checkpoint/deploy/backup-db.sh
sudo crontab -u checkpoint -l 2>/dev/null | cat - <(echo "30 3 * * * /opt/checkpoint/deploy/backup-db.sh") | sudo crontab -u checkpoint -
```
Sichert täglich 03:30 nach `/opt/checkpoint/backups/` (Online-`.backup`, also auch
im laufenden Betrieb konsistent) und hält 14 Tage vor. Backups liegen außerhalb
von Git. Tipp: zusätzlich regelmäßig vom VPS wegkopieren (anderer Ort).
## 11 · Updates einspielen
```fish
cd /opt/checkpoint
sudo -u checkpoint git pull
sudo -u checkpoint .venv/bin/pip install -r requirements.txt
sudo systemctl restart checkpoint
```
Neue Tabellen legt die App beim Start via `create_all()` an. **Achtung:** echte
Schema-*Änderungen* an bestehenden Tabellen macht SQLAlchemy so nicht — dafür
vorher Backup ziehen und ggf. manuell migrieren.
---
## Checkliste (Deliverables Session 15)
- [ ] App läuft unter gunicorn hinter nginx → Schritte 78
- [ ] HTTPS aktiv → Schritt 9
- [ ] Secrets über Umgebungsvariablen, nicht im Code → Schritt 5 (`.env`, systemd `EnvironmentFile`)
- [ ] DB + Uploads persistent, einfaches DB-Backup eingerichtet → `instance/` + Schritt 10
## Fehlersuche
- Dienst-Logs: `sudo journalctl -u checkpoint -e`
- nginx-Logs: `sudo tail -f /var/log/nginx/error.log`
- 413 beim Upload → `client_max_body_size` in nginx prüfen (muss ≥ 10 MB sein).
- 500 direkt nach Deploy → meist fehlendes/falsches `SECRET_KEY` in `.env`
(bei `CP_ENV=production` bricht die App ohne `SECRET_KEY` bewusst ab).
- „database is locked" → sollte dank WAL + busy_timeout nicht auftreten; sonst
Worker-Zahl in `gunicorn.conf.py` senken.
```