5.4 KiB
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 (systemdReadWritePaths).
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 7–8
- HTTPS aktiv → Schritt 9
- Secrets über Umgebungsvariablen, nicht im Code → Schritt 5 (
.env, systemdEnvironmentFile) - 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_sizein nginx prüfen (muss ≥ 10 MB sein). - 500 direkt nach Deploy → meist fehlendes/falsches
SECRET_KEYin.env(beiCP_ENV=productionbricht die App ohneSECRET_KEYbewusst ab). - „database is locked" → sollte dank WAL + busy_timeout nicht auftreten; sonst
Worker-Zahl in
gunicorn.conf.pysenken.