9.5 KiB
Deployment auf cPanel („Setup Python App“ / Phusion Passenger)
Diese Anleitung beschreibt die Installation auf einem Shared-Hosting mit cPanel und CloudLinux-„Setup Python App“. Die App braucht keine Hintergrundprozesse, keinen Redis, keine WebSockets und keine kompilierten Pakete: Flask, python-dotenv und SQLite reichen.
Überblick
| cPanel-Feld | Wert |
|---|---|
| Python version | 3.10 oder neuer (neueste verfügbare wählen) |
| Application root | Ordner im Home-Verzeichnis, z. B. glory_defense |
| Application URL | Domain oder Unterpfad, z. B. example.ch/ oder example.ch/glory |
| Application startup file | passenger_wsgi.py |
| Application Entry point | application |
1. Upload-Paket bauen
Auf dem eigenen Rechner im Projektordner (Windows, Linux oder macOS):
python scripts/build_deploy_zip.py
Das erzeugt dist/glory_defense.zip mit genau den Dateien, die der Server braucht:
passenger_wsgi.py, requirements.txt, .env.example und den Ordner app/
(inkl. app/static/js/lib/phaser.min.js und app/static/assets/). Nicht
enthalten sind .env (Geheimnisse), instance/ (Datenbank), Tests, .venv,
node_modules und Caches. Node.js wird auf dem Server nicht benötigt.
Bitte nicht mit „Komprimierter Ordner“ bzw.
Compress-Archiveaus Windows PowerShell 5.1 packen: Das schreibt Pfade mit Backslash (app\api.py) ins ZIP, und manche Entpacker auf Linux-Servern machen daraus flache Dateinamen. Dann startet die App mitNo module named 'app'nicht. Das Skript verwendet immer/.
Alternativ kann das Repository über cPanel → Git™ Version Control direkt in den Application-Root geklont werden.
2. Python-App anlegen
- cPanel → Software → Setup Python App → Create Application.
- Felder wie in der Tabelle oben ausfüllen.
- Create klicken. cPanel legt den Ordner, eine virtuelle Umgebung
(
~/virtualenv/glory_defense/<version>/) und eine Beispiel-passenger_wsgi.pyan. - Oben auf der Seite erscheint der Befehl zum Aktivieren der venv, etwa:
Diesen Befehl für Schritt 5 kopieren.
source /home/BENUTZER/virtualenv/glory_defense/3.11/bin/activate && cd /home/BENUTZER/glory_defense
3. Dateien hochladen
- cPanel → File Manager → in den Application-Root wechseln (z. B.
~/glory_defense). - Die von cPanel erzeugte
passenger_wsgi.pylöschen oder überschreiben lassen. glory_defense.ziphochladen und dort Extract wählen.- Prüfen:
passenger_wsgi.pyund der Ordnerapp/liegen direkt im Application-Root.
4. Umgebungsvariablen setzen
In „Setup Python App“ bei der App unter Environment variables eintragen und danach Save klicken:
| Name | Wert |
|---|---|
SECRET_KEY |
langer Zufallswert, z. B. Ausgabe von python -c "import secrets; print(secrets.token_hex(32))" |
GLORY_DB_PATH |
optional, Standard instance/glory_defense.sqlite3 (relativ zum Application-Root) |
GLORY_RATE_LIMIT_MAX |
optional, Standard 5 |
GLORY_RATE_LIMIT_WINDOW |
optional, Standard 600 (Sekunden) |
GLORY_TRUST_PROXY |
optional, Standard 0. Nur 1, wenn ein vertrauenswürdiger Proxy (z. B. CDN) davorsteht, siehe „Typische Fehler“ |
GLORY_NAME_BLOCKLIST |
optional, Standard instance/name_blocklist.txt, siehe „Moderation der Ehrentafel“ |
Alternativ eine Datei .env im Application-Root anlegen (Vorlage .env.example).
In cPanel gesetzte Variablen haben Vorrang vor der .env.
Ohne
SECRET_KEYerzeugt die App beim ersten Spielstartinstance/secret_keyselbst. Das funktioniert, ein eigener Wert in cPanel ist aber sauberer und übersteht Neuinstallationen.
5. Abhängigkeiten installieren
Variante A, über die Oberfläche: In „Setup Python App“ bei der App unter
Configuration files requirements.txt eintragen, Add und dann
Run Pip Install klicken.
Variante B, per Terminal (cPanel → Terminal oder SSH):
source /home/BENUTZER/virtualenv/glory_defense/3.11/bin/activate && cd /home/BENUTZER/glory_defense
pip install -r requirements.txt
Die Datenbank legt die App beim ersten Aufruf selbst an, ein eigener Schritt ist nicht nötig.
6. Neustarten und prüfen
- In „Setup Python App“ auf Restart klicken, oder per Terminal:
mkdir -p tmp && touch tmp/restart.txt - Im Browser aufrufen:
https://example.ch/healthzmuss{"status":"ok"}liefernhttps://example.ch/zeigt den Disclaimer und danach das Hauptmenühttps://example.ch/api/highscoresliefert{"highscores":[]}
Läuft die App unter einem Unterpfad (z. B. /glory), funktionieren alle Links
automatisch: Das Template erzeugt URLs über url_for, und Passenger übergibt den
Pfad als SCRIPT_NAME.
7. Updates einspielen
- Lokal
python scripts/build_deploy_zip.pyausführen, das neue ZIP hochladen und entpacken (vorhandene Dateien überschreiben).instance/dabei nicht löschen, denn dort liegen Highscores, Sperrliste und ggf. der automatisch erzeugte Schlüssel. - Falls sich
requirements.txtgeändert hat: Run Pip Install bzw.pip install -r requirements.txt. - Restart (oder
touch tmp/restart.txt). - Im Browser mit Strg+F5 neu laden, damit keine alten JavaScript-Dateien aus dem Cache kommen.
8. Datensicherung
Zu sichern ist nur der Ordner instance/:
instance/glory_defense.sqlite3: Highscoresinstance/name_blocklist.txt: Sperrliste, falls angelegtinstance/secret_key: nur vorhanden, wenn keinSECRET_KEYgesetzt ist
9. Moderation der Ehrentafel
Spielernamen erscheinen öffentlich. Unpassende Einträge (Beleidigungen, Namen realer Personen) lassen sich im Terminal löschen. Zuerst die venv aktivieren (Befehl aus Schritt 2.4), dann:
flask --app passenger_wsgi scores list # Einträge mit ID anzeigen
flask --app passenger_wsgi scores delete 17 23 # Einträge per ID löschen
flask --app passenger_wsgi scores delete-name "Name" # alle Einträge mit diesem Namen löschen
Die Befehle brauchen keinen SECRET_KEY. Das ist wichtig, denn in cPanel gesetzte
Umgebungsvariablen stehen im Terminal nicht zur Verfügung. Eine .env im
Application-Root wird dagegen auch hier gelesen.
Sperrliste (optional): Die Datei instance/name_blocklist.txt anlegen, ein Begriff
pro Zeile, # leitet Kommentare ein. Namen, die einen dieser Begriffe enthalten,
lehnt die API ab. Groß-/Kleinschreibung, Leerzeichen und Satzzeichen spielen dabei
keine Rolle, „Max Muster“ sperrt also auch „max.muster“. Die Datei wird bei jeder
Einreichung neu gelesen, ein Neustart ist nicht nötig.
Typische Fehler
| Symptom | Ursache und Lösung |
|---|---|
| „Incomplete response received from application“ oder Fehler 500 | Details stehen in stderr.log im Application-Root bzw. in cPanel → Errors. Meist fehlen die Abhängigkeiten (Schritt 4) oder die Startdatei ist falsch eingetragen. |
ModuleNotFoundError: No module named 'app' |
passenger_wsgi.py liegt nicht im Application-Root, der Ordner app/ fehlt bzw. liegt eine Ebene zu tief (ZIP in einen Unterordner entpackt), oder das ZIP wurde mit Backslash-Pfaden erstellt (Dateien heißen dann app\api.py). Paket mit python scripts/build_deploy_zip.py bauen. |
ModuleNotFoundError: No module named 'flask' |
Pip-Install lief in der falschen Umgebung. Befehl aus Schritt 2.4 verwenden (venv aktivieren) und erneut installieren (Schritt 5). |
SyntaxError oder Flask-Importfehler beim Start |
Python-Version zu alt. In „Setup Python App“ 3.10 oder neuer wählen (die App kann dafür neu angelegt werden, Dateien bleiben erhalten). |
sqlite3.OperationalError: unable to open database file |
Der Ordner der Datenbank ist nicht beschreibbar. instance/ muss dem cPanel-Benutzer gehören (Rechte 755). GLORY_DB_PATH nicht auf einen fremden Ordner setzen. |
database is locked bei viel Last |
SQLite wartet bis zu 10 s auf Sperren. Bei sehr vielen gleichzeitigen Einreichungen hilft ein kleineres Rate-Limit. Für ein Spiel mit Highscore-Liste reicht SQLite in der Regel. |
Seite lädt, Spiel bleibt schwarz, Konsole zeigt 404 für phaser.min.js |
app/static/js/lib/phaser.min.js wurde nicht mit hochgeladen (manche FTP-Clients überspringen große Dateien). Das Upload-ZIP aus Schritt 1 enthält sie. |
| Änderungen nicht sichtbar | App neu starten (Schritt 6) und Browser-Cache leeren (Strg+F5). |
| Alle Spieler teilen sich ein Rate-Limit | Ein vorgeschalteter Proxy (z. B. CDN) liefert allen dieselbe IP. Wenn der Proxy X-Forwarded-For zuverlässig setzt, GLORY_TRUST_PROXY=1 setzen. Sonst beim Hoster mod_remoteip erfragen. |
/healthz ok, aber Highscores werden mit „Run-Token ungültig“ abgelehnt |
SECRET_KEY wurde zwischen Spielstart und Einreichen geändert (z. B. durch Neustart mit neuem Wert). Einfach eine neue Runde spielen. |
Sicherheitshinweise
- Die App setzt eine strikte Content-Security-Policy. Das Spiel lädt keine Skripte von fremden Servern.
- IP-Adressen werden nicht gespeichert, nur ein HMAC-Hash (mit
SECRET_KEY) für das Rate-Limit. Einträge werden nach Ablauf des Zeitfensters automatisch gelöscht. - Spielernamen werden bereinigt (max. 16 Zeichen, keine Steuer- oder Sonderzeichen) und im Frontend immer als Text ausgegeben, nie als HTML. Unpassende Einträge lassen sich löschen oder per Sperrliste verhindern (Abschnitt 9).
- Mit
GLORY_TRUST_PROXY=1übernimmt die App vom Proxy nur Client-IP und Protokoll, nicht Host oder Pfadpräfix. So lassen sich keine fremden URLs in die Seite schleusen.