# VPS_DEPLOY.md — Projekt-Hub auf dem VPS deployen (Docker + Caddy) Deployment-Anleitung für den IONOS VPS (Debian 13, `patsy`, Docker + Caddy). Diese Anleitung ersetzt `DEPLOYMENT.md` für diesen Server. **Zielarchitektur:** ``` Internet → Caddy (Port 80/443, TLS automatisch) → projekt-hub:8000 (Docker) → SQLite (Volume) ``` **Voraussetzungen:** - SSH-Zugang als `patsy` funktioniert - Docker und die `proxy`-Netzwerk-Infrastruktur laufen (`caddy` + `proxy`-Netz) - Code ist im lokalen Repo fertig und committed --- ## 1. Code auf den Server bringen ### Option A: Über Forgejo (git.bujour.de) — empfohlen Zuerst lokal ins Forgejo pushen: ```bash # Einmalig: Remote hinzufügen (falls noch nicht geschehen) git remote add forgejo ssh://git@git.bujour.de:2222/patsy/projekt-hub.git git push forgejo main ``` Dann auf dem VPS: ```bash ssh patsy@ mkdir -p ~/apps git clone ssh://git@git.bujour.de:2222/patsy/projekt-hub.git ~/apps/projekt-hub ``` ### Option B: Dateien direkt hochladen ```bash # Lokal ausführen: scp -r /pfad/zum/projekt-hub patsy@:~/apps/projekt-hub ``` --- ## 2. Docker-Image bauen ```bash cd ~/apps/projekt-hub docker build -t projekt-hub:latest . ``` Dauert beim ersten Mal ~2 Minuten (Dependencies werden heruntergeladen und gecacht). Danach prüfen, dass das Image existiert: ```bash docker images | grep projekt-hub ``` --- ## 3. Verzeichnisse anlegen ```bash mkdir -p ~/apps/projekt-hub/instance mkdir -p ~/stacks/projekt-hub ``` --- ## 4. `.env`-Datei anlegen ```bash nano ~/apps/projekt-hub/.env ``` Inhalt (Werte anpassen): ```ini FLASK_ENV=production SECRET_KEY= # Datenbank (relative sqlite-Namen werden in instance/ aufgelöst) DATABASE_URL=sqlite:///hub.sqlite # Auth — nur diese E-Mail darf sich einloggen ALLOWED_EMAILS=deine@adresse.com # Login-Code-Einstellungen LOGIN_CODE_TTL_MINUTES=10 LOGIN_CODE_RATE_LIMIT=5 per hour # Rate-Limiting: memory:// reicht für Einzelnutzer / einen Worker RATELIMIT_STORAGE_URI=memory:// # SMTP (siehe Abschnitt 7) MAIL_SERVER=smtp.infomaniak.com MAIL_PORT=587 MAIL_USE_TLS=true MAIL_USE_SSL=false MAIL_USERNAME=deine@adresse.com MAIL_PASSWORD= MAIL_DEFAULT_SENDER=deine@adresse.com # Cookies: Secure-Flag (HTTPS läuft über Caddy) SESSION_COOKIE_SECURE=true # SERVER_NAME leer lassen — Caddy übernimmt die Domain SERVER_NAME= ``` Secret Key generieren: ```bash python3 -c "import secrets; print(secrets.token_hex(32))" ``` Datei absichern: ```bash chmod 600 ~/apps/projekt-hub/.env ``` --- ## 5. Datenbank initialisieren Die Migration einmalig in einem kurzlebigen Container ausführen: ```bash docker run --rm \ --env-file /home/patsy/apps/projekt-hub/.env \ -v /home/patsy/apps/projekt-hub/instance:/app/instance \ projekt-hub:latest \ flask db upgrade ``` Prüfen, dass die SQLite-Datei angelegt wurde: ```bash ls -lh ~/apps/projekt-hub/instance/ # → hub.sqlite sollte erscheinen ``` --- ## 6. Compose-Stack einrichten und starten `compose.yaml` aus dem Repo-Template kopieren: ```bash cp ~/apps/projekt-hub/deploy/compose.yaml ~/stacks/projekt-hub/compose.yaml ``` Stack starten: ```bash cd ~/stacks/projekt-hub docker compose up -d ``` Container-Log prüfen (sollte "Listening at: http://0.0.0.0:8000" zeigen): ```bash docker logs projekt-hub ``` --- ## 7. Caddy konfigurieren Einen Block zur Caddyfile hinzufügen: ```bash nano ~/stacks/caddy/Caddyfile ``` Block anfügen (Subdomain nach Wunsch wählen, z.B. `hub.bujour.de`): ``` hub.bujour.de { reverse_proxy projekt-hub:8000 } ``` Caddy neu laden — kein Neustart nötig, Caddy fetcht das TLS-Zertifikat automatisch: ```bash docker exec caddy caddy reload --config /etc/caddy/Caddyfile ``` Im Browser `https://hub.bujour.de` öffnen — die Login-Seite sollte erscheinen. > **DNS:** Der A-Record `hub.bujour.de → ` muss im DNS-Provider gesetzt sein, > bevor Caddy das Zertifikat holen kann. Vorher ist die App nur intern erreichbar. --- ## 8. E-Mail-Versand einrichten ### Empfehlung: externer EU-SMTP-Relay (Infomaniak, Mailjet EU) Am einfachsten und zuverlässigsten für Docker: ein externer SMTP-Anbieter. SMTP-Zugangsdaten im Anbieter-Konto anlegen und in `.env` eintragen (Abschnitt 4). Am Flask-Code ändert sich nichts — die App liest alles aus der Umgebung. Testmail aus einem kurzlebigen Container: ```bash docker run --rm \ --env-file /home/patsy/apps/projekt-hub/.env \ projekt-hub:latest \ python3 -c " from dotenv import load_dotenv from app import create_app from app.extensions import mail from flask_mail import Message app = create_app('production') with app.app_context(): msg = Message('Testmail Projekt-Hub', recipients=['deine@adresse.com']) msg.body = 'Mailversand funktioniert.' mail.send(msg) print('Mail verschickt.') " ``` ### Alternative: Proton Bridge in Docker Falls Proton Mail Bridge gewünscht: als eigenen Stack deployen und `MAIL_SERVER=proton-bridge` in `.env` setzen (Container-Name als Hostname). Proton Bridge muss im selben Docker-Netzwerk oder einem gemeinsamen Netzwerk laufen. Details: `DEPLOYMENT.md §7` (Vorsicht: `127.0.0.1` funktioniert in Docker-Containern **nicht** als SMTP-Adresse — immer Container-Name oder externe IP verwenden). --- ## 9. Abschluss-Test: Login-Flow 1. `https://hub.bujour.de` öffnen 2. E-Mail-Adresse aus `ALLOWED_EMAILS` eingeben und absenden 3. Postfach prüfen — 6-stelliger Code sollte innerhalb weniger Sekunden ankommen 4. Code auf der Verify-Seite eingeben 5. Weiterleitung zur Projektübersicht → fertig --- ## 10. Sicherheits-Checkliste - [ ] `.env` hat restriktive Rechte: `chmod 600 ~/apps/projekt-hub/.env` - [ ] `FLASK_ENV=production` und `SECRET_KEY` sind gesetzt (kein Default) - [ ] `SESSION_COOKIE_SECURE=true` ist gesetzt (HTTPS läuft via Caddy) - [ ] UFW: Ports 22, 80, 443 offen — **kein** direkter Zugriff auf Port 8000 von außen - [ ] IONOS-Hardware-Firewall ebenfalls prüfen (nur 22, 80, 443 erlaubt) - [ ] `.env` ist in `.gitignore` — niemals committen UFW-Status prüfen: ```bash sudo ufw status ``` Port 8000 ist nur intern (Docker `proxy`-Netz) erreichbar — das ist korrekt und gewollt. --- ## 11. Updates einspielen ```bash cd ~/apps/projekt-hub git pull # Image neu bauen docker build -t projekt-hub:latest . # Migrationen (falls neue vorhanden) docker run --rm \ --env-file /home/patsy/apps/projekt-hub/.env \ -v /home/patsy/apps/projekt-hub/instance:/app/instance \ projekt-hub:latest \ flask db upgrade # Container neu starten cd ~/stacks/projekt-hub docker compose up -d --force-recreate ``` --- ## 12. Backup Die gesamte App-Datenbank ist eine einzelne SQLite-Datei. Tägliches Backup per Cronjob: ```bash crontab -e ``` Zeile hinzufügen: ``` 0 3 * * * sqlite3 /home/patsy/apps/projekt-hub/instance/hub.sqlite ".backup '/home/patsy/backups/hub-$(date +\%F).sqlite'" && find /home/patsy/backups -name "hub-*.sqlite" -mtime +30 -delete ``` Backup-Verzeichnis anlegen: ```bash mkdir -p ~/backups ``` `sqlite3 .backup` ist konsistenter als einfaches `cp` bei laufender App. --- ## Anhang: Häufige Befehle ```bash # Logs ansehen docker logs -f projekt-hub # Container neu starten docker compose -f ~/stacks/projekt-hub/compose.yaml restart # Container stoppen / starten docker compose -f ~/stacks/projekt-hub/compose.yaml down docker compose -f ~/stacks/projekt-hub/compose.yaml up -d # Image neu bauen und Container ersetzen cd ~/apps/projekt-hub && docker build -t projekt-hub:latest . && \ cd ~/stacks/projekt-hub && docker compose up -d --force-recreate # Flask-Shell für Debugging docker run --rm -it \ --env-file /home/patsy/apps/projekt-hub/.env \ -v /home/patsy/apps/projekt-hub/instance:/app/instance \ projekt-hub:latest \ flask shell ```