Project_Manager/VPS_DEPLOY.md
2026-06-26 19:42:21 +02:00

7.7 KiB

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:

# 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:

ssh patsy@<SERVER-IP>
mkdir -p ~/apps
git clone ssh://git@git.bujour.de:2222/patsy/projekt-hub.git ~/apps/projekt-hub

Option B: Dateien direkt hochladen

# Lokal ausführen:
scp -r /pfad/zum/projekt-hub patsy@<SERVER-IP>:~/apps/projekt-hub

2. Docker-Image bauen

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:

docker images | grep projekt-hub

3. Verzeichnisse anlegen

mkdir -p ~/apps/projekt-hub/instance
mkdir -p ~/stacks/projekt-hub

4. .env-Datei anlegen

nano ~/apps/projekt-hub/.env

Inhalt (Werte anpassen):

FLASK_ENV=production
SECRET_KEY=<langer-zufaelliger-string>

# 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=<smtp-passwort>
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:

python3 -c "import secrets; print(secrets.token_hex(32))"

Datei absichern:

chmod 600 ~/apps/projekt-hub/.env

5. Datenbank initialisieren

Die Migration einmalig in einem kurzlebigen Container ausführen:

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:

ls -lh ~/apps/projekt-hub/instance/
# → hub.sqlite sollte erscheinen

6. Compose-Stack einrichten und starten

compose.yaml aus dem Repo-Template kopieren:

cp ~/apps/projekt-hub/deploy/compose.yaml ~/stacks/projekt-hub/compose.yaml

Stack starten:

cd ~/stacks/projekt-hub
docker compose up -d

Container-Log prüfen (sollte "Listening at: http://0.0.0.0:8000" zeigen):

docker logs projekt-hub

7. Caddy konfigurieren

Einen Block zur Caddyfile hinzufügen:

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:

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 → <SERVER-IP> 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:

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:

sudo ufw status

Port 8000 ist nur intern (Docker proxy-Netz) erreichbar — das ist korrekt und gewollt.


11. Updates einspielen

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:

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:

mkdir -p ~/backups

sqlite3 .backup ist konsistenter als einfaches cp bei laufender App.


Anhang: Häufige Befehle

# 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