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

5.4 KiB
Raw Permalink Blame History

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

sudo apt update
sudo apt install -y python3 python3-venv python3-pip nginx sqlite3 git certbot python3-certbot-nginx

2 · Dienst-Nutzer + Verzeichnis

sudo useradd --system --create-home --home-dir /opt/checkpoint --shell /usr/sbin/nologin checkpoint
sudo mkdir -p /opt/checkpoint

3 · Code holen

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

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:

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

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

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

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)

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

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

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.