- Compose reicht HTTP_PROXY/HTTPS_PROXY/NO_PROXY als Build-Argumente an beide Images weiter (npm ci, Google Fonts) und zur Laufzeit an api und worker; api, db, web stehen immer in NO_PROXY - Spielerköpfe werden über undici EnvHttpProxyAgent geladen (Node-fetch ignoriert Proxy-Variablen) - Doku: Proxy-Abschnitt mit Whitelist und Fehlerbildern - geprüft mit lokalem Squid: Build nur über registry.npmjs.org, fonts.googleapis.com, fonts.gstatic.com; zur Laufzeit nur mc-heads.net Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 KiB
Betrieb
Für den Betreiber des Portals: Installation auf dem DMZ-Server hinter OPNsense und Traefik, Einspielen der Mod bei Mine-Hoster, laufender Betrieb und Fehlerbehebung.
1. Voraussetzungen
- Server in der DMZ mit Docker und Docker Compose v2
- Traefik v3 mit Docker-Provider, einem HTTPS-Entrypoint und Cert-Resolver; Traefik hängt an einem externen
Docker-Netz (z. B.
traefik) - DNS-Eintrag für die Portal-Domain, der auf die öffentliche IP der OPNsense zeigt
- Zugang zum Mine-Hoster-Gamepanel (SFTP-Daten unter Einstellungen → SFTP-Details)
Traefik v2: Die Middleware heißt dort
ipwhiteliststattipallowlist. Indeploy/docker-compose.ymldie Zeiletraefik.http.middlewares.atm-ingest-allow.ipallowlist.sourcerangeentsprechend umbenennen.
Ausgehender Proxy (z. B. Squid in der DMZ)
Muss der Server über einen Proxy ins Internet, trage ihn in deploy/.env ein:
HTTP_PROXY=http://squid-proxy.example.local:3128
HTTPS_PROXY=http://squid-proxy.example.local:3128
NO_PROXY=localhost,127.0.0.1,.local
Compose reicht die Werte als Build-Argumente an beide Images und zur Laufzeit an api und worker weiter. Die
Container-Namen api, db und web hängt es automatisch an NO_PROXY an, sodass interner Verkehr nie über den
Proxy läuft. Eine Proxy-Einstellung für den Docker-Daemon (/etc/systemd/system/docker.service.d/proxy.conf) gilt
nur für Image-Pulls. Für npm ci im Build reicht sie nicht; ohne die Einträge oben bricht der Build mit
„npm error Exit handler never called!“ ab.
Whitelist für den Proxy (alle Verbindungen sind CONNECT …:443):
| Domain | Wofür | Wann |
|---|---|---|
registry.npmjs.org |
npm-Pakete | Build, Pflicht |
fonts.googleapis.com, fonts.gstatic.com |
Schriften des Dashboards | Build, Pflicht |
registry-1.docker.io, auth.docker.io, production.cloudflare.docker.com |
Basis-Images (über die Daemon-Einstellung) | Pull, Pflicht |
mc-heads.net |
Spielerköpfe | Laufzeit, optional (ohne: graue Platzhalter) |
Der SFTP-Abgleich zum Gameserver ist eine direkte SSH-Verbindung und läuft nicht über den Proxy; dafür braucht der DMZ-Server eine eigene Firewall-Freigabe (siehe OPNsense).
2. Portal installieren
git clone <repo> atm10-stats && cd atm10-stats
cp deploy/.env.example deploy/.env
openssl rand -hex 32 # Wert für INGEST_HMAC_SECRET
openssl rand -base64 24 # Wert für POSTGRES_PASSWORD
nano deploy/.env # DOMAIN, TRAEFIK_*, INGEST_ALLOWED_IPS, Secrets, optional SFTP_*
docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --build
docker compose -f deploy/docker-compose.yml --env-file deploy/.env ps # api, web, db: healthy
Admin-Konto anlegen (Passwort mindestens 12 Zeichen, wird interaktiv abgefragt) und Ingest-Token für die Mod erzeugen:
docker compose -f deploy/docker-compose.yml --env-file deploy/.env exec api node dist/cli.js admin:create lucas
docker compose -f deploy/docker-compose.yml --env-file deploy/.env exec api node dist/cli.js token:create
Das Token wird nur einmal angezeigt. Später lässt es sich unter /admin erneuern.
Routing
| Pfad | Ziel | Hinweis |
|---|---|---|
https://DOMAIN/api/ingest* |
api:3001 | nur von INGEST_ALLOWED_IPS (Middleware atm-ingest-allow) |
https://DOMAIN/api/* |
api:3001 | öffentliche Lese-API, /api/live (SSE), /api/admin/* |
https://DOMAIN/* |
web:3000 | Dashboard |
/api/live ist ein Server-Sent-Events-Stream. Auf diese Route keine Kompressions- oder Buffering-Middleware legen.
Die API sendet dafür X-Accel-Buffering: no und alle 25 s einen Heartbeat.
OPNsense
- Eingehend: HTTPS (443) zur DMZ/Traefik wie für andere Dienste. Die Einschränkung von
/api/ingestauf den Gameserver übernimmt Traefik (INGEST_ALLOWED_IPS). Die IP des Mine-Hoster-Servers ergibt sich aus der Serveradresse im Gamepanel (Hostname pernslookupauflösen). Optional zusätzlich in OPNsense: Alias mit dieser IP und eine eigene Regel nur für den Ingest-Host. - Ausgehend (nur für den SFTP-Abgleich): DMZ-Server → SFTP-Host/Port des Mine-Hoster-Servers (TCP).
Ohne diese Regel meldet
/sources„Fehler beim letzten Abgleich“.
3. Mod bei Mine-Hoster einspielen
Die Mod ist rein serverseitig: Spieler installieren nichts, Clients ohne die Mod können normal verbinden.
- Mod bauen (oder fertiges JAR aus einem Release verwenden):
./mod/gradlew -p mod build # → mod/build/libs/atmstats-1.0.0.jar - Im Gamepanel den Server stoppen.
- Per SFTP (WinSCP/FileZilla)
atmstats-1.0.0.jarnachmods/hochladen. - Server starten und nach dem Start wieder stoppen. Dabei entsteht
config/atmstats-server.toml. - Konfiguration per SFTP bearbeiten:
[atmstats] enabled = true endpoint = "https://stats.example.de" serverId = "atm10" token = "<Token aus token:create>" hmacSecret = "<INGEST_HMAC_SECRET aus deploy/.env>" statsSnapshotMinutes = 5 questScanMinutes = 2 healthIntervalSeconds = 60 maxOutboxMb = 50 - Server starten und in
logs/latest.lognachatmstats:suchen:atmstats: backend reachable: fertig, Daten laufen.atmstats: backend NOT reachable: …: siehe Abschnitt 6. Die Ereignisse bleiben inatmstats/outbox/auf dem Gameserver und werden gesendet, sobald die Verbindung klappt.
Ohne endpoint, token oder hmacSecret bleibt die Mod passiv und schreibt nur eine Hinweiszeile ins Log.
ATM10-Updates: Die Mod verwendet nur APIs, die in NeoForge 21.1.x stabil sind, und deklariert NeoForge ab
21.1.0. Bei Pack-Updates innerhalb von Minecraft 1.21.1 bleibt das JAR gültig. Der Mod-Ordner wird bei
Modpack-Updates manchmal ersetzt: danach prüfen, ob mods/atmstats-*.jar noch vorhanden ist.
4. SFTP-Abgleich (optional, empfohlen)
In deploy/.env SFTP_HOST, SFTP_PORT, SFTP_USER, SFTP_PASSWORD setzen (Gamepanel → Einstellungen →
SFTP-Details). SFTP_ROOT ist das Serververzeichnis, SFTP_WORLD_DIR der Weltordner (Standard world,
level-name aus server.properties). Danach docker compose … up -d worker.
Der Worker holt alle 15 Minuten nur geänderte Dateien. Unter /admin lässt sich ein Abgleich sofort anstoßen.
Ohne funktionierende Mod liefert SFTP trotzdem Gesamtzähler, Advancements, Quests und (aus dem Log) Beitritte;
Echtzeit-Daten, Todesursachen und Serverleistung fehlen dann.
Das SFTP-Passwort ist das des Gamepanels und gewährt Schreibzugriff. Der Worker liest ausschließlich.
5. Laufender Betrieb
Updates
git pull
docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --build
Datenbankmigrationen laufen beim Start von api und worker automatisch (serialisiert über einen Advisory-Lock).
Token und Secret erneuern
- Ingest-Token:
/admin→ Ingest-Token erneuern. Das alte Token wird sofort ungültig. Das neue inconfig/atmstats-server.tomleintragen und den Gameserver neu starten. Bis dahin puffert die Mod und meldetauthentication failedim Log; es geht nichts verloren, solange das Outbox-Limit nicht erreicht ist. - HMAC-Secret: in
deploy/.envund in der Mod-Konfiguration gleichzeitig ändern, dann api und Gameserver neu starten.
Backups
Der Container backup erstellt täglich pg_dump-Dateien im Volume backups und löscht sie nach
BACKUP_KEEP_DAYS (Standard 14). Sofortiges Backup:
docker compose -f deploy/docker-compose.yml --env-file deploy/.env exec backup sh /backup.sh once
Wiederherstellung (ersetzt alle Daten):
C="docker compose -f deploy/docker-compose.yml --env-file deploy/.env"
$C stop api worker web
$C exec backup ls -t /backups # gewünschte Datei wählen
$C exec backup dropdb atm
$C exec backup createdb atm
$C exec backup pg_restore --dbname=atm --no-owner /backups/<datei>.dump
$C start api worker web
Das Volume backups liegt auf demselben Host. Für echte Ausfallsicherheit die Dateien zusätzlich extern sichern
(z. B. per Borg/Restic vom Docker-Volume-Pfad).
Verhalten bei Neustarts
| Situation | Verhalten |
|---|---|
| Gameserver stoppt normal | Mod schreibt player_leave und server_stop in die Outbox und sendet sie beim nächsten Start; nach 10 min ohne Health-Meldung schließt der Worker offene Sitzungen |
| Gameserver stürzt ab | beim nächsten Start schließt server_start offene Sitzungen zum letzten Lebenszeichen (kein Aufblähen der Spielzeit) |
| Backend nicht erreichbar | Mod puffert auf der Festplatte (max. maxOutboxMb, 7 Tage), sendet mit Backoff 1 s … 5 min weiter; bei Platzmangel werden zuerst alte Statistik-Snapshots verworfen |
| Doppelte Lieferung | jede Meldung hat eine eindeutige ID, Duplikate werden ignoriert |
| API/Worker-Neustart | zustandslos; der Worker setzt den Log-Abgleich am gespeicherten Byte-Offset fort |
6. Fehlerbilder
| Meldung | Ursache | Lösung |
|---|---|---|
Mod: backend NOT reachable: …ConnectException / HttpTimeoutException |
Mine-Hoster blockiert ausgehende Verbindungen, DNS falsch oder OPNsense/Traefik lässt nicht durch | curl -v https://DOMAIN/api/health von außen testen; Traefik-Log prüfen (403 = IP nicht in INGEST_ALLOWED_IPS); falls der Hoster ausgehend blockiert: enabled = false und nur SFTP nutzen |
Mod: clock skew: server clock differs … |
Uhr des Gameservers oder des DMZ-Servers weicht > 5 min ab | NTP auf dem DMZ-Server prüfen; beim Hoster melden, falls der Gameserver falsch geht |
Mod: authentication failed (bad_token) |
Token falsch oder widerrufen | neues Token erzeugen und eintragen |
Mod: authentication failed (bad_signature) |
hmacSecret ≠ INGEST_HMAC_SECRET |
Werte angleichen |
Mod: batch rejected by backend … moved to outbox/rejected |
Daten passten nicht zum Protokoll (z. B. veraltete Mod-Version) | Datei in atmstats/outbox/rejected/ samt .reason.txt prüfen, Mod und Backend auf gleichen Stand bringen |
Mod: outbox limit reached, dropped … |
Backend länger nicht erreichbar | Verbindung reparieren; ggf. maxOutboxMb erhöhen |
/sources: SFTP „Fehler beim letzten Abgleich“ |
Zugangsdaten, Port oder OPNsense-Regel | Fehlertext unter /admin → Letzte Fehler |
| Live-Ticker bleibt leer, Seite sonst ok | Proxy puffert SSE | keine Kompressions-/Buffering-Middleware auf /api/live |
Build: npm error Exit handler never called! nach ~2 min |
npm ci erreicht registry.npmjs.org nicht (ausgehender Proxy/Firewall) |
Proxy in deploy/.env eintragen und Domains freischalten (Abschnitt 1, „Ausgehender Proxy“) |
| Build: Fehler beim Laden von Google Fonts | fonts.googleapis.com/fonts.gstatic.com nicht freigeschaltet |
in der Proxy-Whitelist ergänzen |
| Nur graue Spielerköpfe | mc-heads.net nicht erreichbar |
in der Proxy-Whitelist ergänzen (optional) |
Login unter /admin klappt nicht ohne HTTPS |
Cookie ist Secure |
Portal über HTTPS aufrufen (lokal: NODE_ENV=development) |
Spieler, die nicht im Portal erscheinen möchten, lassen sich unter /admin ausblenden: Sie verschwinden aus allen
öffentlichen Ansichten, Ranglisten und Live-Meldungen. Die Daten bleiben gespeichert, sodass sich das Ausblenden
rückgängig machen lässt.