Files
minecraft-atm10-stats/docs/OPERATIONS.md
T
lucashahmannandClaude Opus 5.5 d45d1821df feat(deploy): ausgehender HTTP(S)-Proxy für Build und Laufzeit
- 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>
2026-10-13 10:35:46 +02:00

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 ipwhitelist statt ipallowlist. In deploy/docker-compose.yml die Zeile traefik.http.middlewares.atm-ingest-allow.ipallowlist.sourcerange entsprechend 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

  1. Eingehend: HTTPS (443) zur DMZ/Traefik wie für andere Dienste. Die Einschränkung von /api/ingest auf den Gameserver übernimmt Traefik (INGEST_ALLOWED_IPS). Die IP des Mine-Hoster-Servers ergibt sich aus der Serveradresse im Gamepanel (Hostname per nslookup auflösen). Optional zusätzlich in OPNsense: Alias mit dieser IP und eine eigene Regel nur für den Ingest-Host.
  2. 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.

  1. Mod bauen (oder fertiges JAR aus einem Release verwenden):
    ./mod/gradlew -p mod build        # → mod/build/libs/atmstats-1.0.0.jar
    
  2. Im Gamepanel den Server stoppen.
  3. Per SFTP (WinSCP/FileZilla) atmstats-1.0.0.jar nach mods/ hochladen.
  4. Server starten und nach dem Start wieder stoppen. Dabei entsteht config/atmstats-server.toml.
  5. 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
    
  6. Server starten und in logs/latest.log nach atmstats: suchen:
    • atmstats: backend reachable: fertig, Daten laufen.
    • atmstats: backend NOT reachable: …: siehe Abschnitt 6. Die Ereignisse bleiben in atmstats/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 in config/atmstats-server.toml eintragen und den Gameserver neu starten. Bis dahin puffert die Mod und meldet authentication failed im Log; es geht nichts verloren, solange das Outbox-Limit nicht erreicht ist.
  • HMAC-Secret: in deploy/.env und 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.