monitor/docker-compose.yml
fds2610-bot dee0f5f304 Restart-Policy fuer api und web, damit der Monitor nach Reboot/OOM zurueckkommt
Bisher hatte nur der Service sync ein "restart: unless-stopped". Nach einem
Reboot, einem OOM-Kill (512 MiB RAM in der Produktion) oder einem Docker-Neustart
kam deshalb nur sync von allein zurueck, waehrend api (8091) und web (8090)
liegen blieben. "docker compose ps" zeigte dann genau einen laufenden Container
und der Monitor war ueber die Firewall-Weiterleitung nicht mehr erreichbar - ein
Fehlerbild, das nach Firewall/Reverse Proxy aussieht, obwohl schlicht nichts auf
den Ports lauschte.

unless-stopped statt always, damit ein bewusstes "docker compose stop" bestehen
bleibt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 13:47:40 +02:00

113 lines
5.7 KiB
YAML

services:
api:
build:
context: ./api
dockerfile: docker/Dockerfile
volumes:
- ./api:/app
# Optional: echte status.txt aus der Produktion zum Testen einbinden, z.B.
# - ./status.txt:/status.txt:ro
working_dir: /app
# Ohne Restart-Policy bleiben api und web nach einem Reboot, einem OOM-Kill (die
# Produktionsmaschine hat 512 MiB RAM) oder einem Docker-Neustart einfach liegen,
# waehrend sync von allein zurueckkommt. "docker compose ps" zeigt dann genau einen
# laufenden Container und der Monitor antwortet gar nicht mehr - der Fehler sieht nach
# Firewall/Proxy aus, obwohl schlicht nichts auf 8090/8091 lauscht.
# unless-stopped statt always: ein bewusstes "docker compose stop" bleibt bestehen.
restart: unless-stopped
# Achtung: Eine Aenderung des Host-Ports muss in .env (MONITOR_API_BASE) nachgezogen
# werden - das Frontend laeuft im Browser und erreicht die API nicht ueber das
# Compose-Netzwerk, sondern ueber genau diesen Port.
ports:
- "8091:8080"
environment:
# Der eingebaute PHP-Server arbeitet Anfragen sonst streng nacheinander ab: eine einzige
# offene SSE-Verbindung (GET /api/v1/stream, bis zu 60s) blockiert damit jede weitere
# Anfrage - gemessen 16s fuer ein GET /api/v1/tickets. Ab dem zweiten offenen Monitor
# laeuft die API praktisch dauerhaft dicht, das Frontend faellt auf Demo-Daten zurueck
# und der PO reagiert dann auf nichts mehr (Fehlerbericht zu Issue #27).
PHP_CLI_SERVER_WORKERS: 4
MONITOR_DB_PATH: /app/data/monitor.sqlite
MONITOR_ADMIN_API_KEY: ${MONITOR_ADMIN_API_KEY:-dev-admin-key}
MONITOR_STATUS_FILE: /status.txt
MONITOR_DESK_COUNT: 8
MONITOR_SSE_MAX_ITERATIONS: 30
MONITOR_SSE_POLL_INTERVAL: 2
MONITOR_SYNC_TRIGGER_FILE: /app/data/sync-requested
CODEBERG_API_BASE: https://codeberg.org/api/v1
CODEBERG_TOKEN: ${CODEBERG_TOKEN:-}
# Secret des Codeberg-Webhooks. Ohne diesen Wert weist POST /api/v1/webhooks/codeberg
# jede Anfrage mit 401 ab - der Endpunkt ist aus dem Internet erreichbar und die
# Signatur ist sein einziger Schutz.
CODEBERG_WEBHOOK_SECRET: ${CODEBERG_WEBHOOK_SECRET:-}
# Zaehldatei fuer den Hooks-KPI (Issue #38, GET /api/v1/webhooks/stats): wird bei jedem
# Start dieses Containers von entrypoint.sh auf 0 zurueckgesetzt.
MONITOR_HOOKS_COUNTER_FILE: /app/data/hooks-count
# Haelt Backlog/Done aus Codeberg aktuell (F4, siehe API.md Abschnitt "Codeberg-Sync"):
# laeuft dasselbe Image wie api und teilt sich ueber das ./api-Volume dieselbe SQLite-Datei.
# Ohne diesen Service bleibt das Board leer, solange kein Agent selbst Tickets meldet.
#
# Der Watch-Modus laeuft aus zwei Gruenden: periodisch alle MONITOR_SYNC_INTERVAL Sekunden
# und sofort, wenn der api-Container ueber die Trigger-Datei einen Codeberg-Webhook
# weiterreicht (siehe Sync\SyncTrigger). Mit funktionierendem Webhook ist das Intervall nur
# noch das Sicherheitsnetz fuer verpasste Zustellungen und darf deshalb gross sein.
#
# Dieser Service selbst baut weiterhin nur ausgehende Verbindungen auf und braucht kein
# Port-Mapping. Die eingehende Freigabe betrifft ausschliesslich den api-Service, ueber den
# Codeberg den Webhook zustellt.
sync:
build:
context: ./api
dockerfile: docker/Dockerfile
volumes:
- ./api:/app
working_dir: /app
restart: unless-stopped
depends_on:
- api
environment:
MONITOR_DB_PATH: /app/data/monitor.sqlite
MONITOR_SYNC_INTERVAL: ${MONITOR_SYNC_INTERVAL:-600}
MONITOR_SYNC_TRIGGER_FILE: /app/data/sync-requested
CODEBERG_API_BASE: https://codeberg.org/api/v1
CODEBERG_TOKEN: ${CODEBERG_TOKEN:-}
command: php bin/sync-codeberg.php --watch
# Statisches Frontend (html/) - Phase 2 der Neuausrichtung (Buero-Visualisierung).
# Nur zur lokalen Ansicht/Entwicklung, kein Build-Schritt noetig (Vanilla HTML/CSS/JS).
web:
image: nginx:alpine
# Siehe api: ohne Restart-Policy kommt das Frontend nach Reboot/OOM nicht von allein
# zurueck - und genau dieser Container bedient den nach aussen freigegebenen Port 8090.
restart: unless-stopped
volumes:
- ./html:/usr/share/nginx/html:ro
# Ueberlagert den Platzhalter html/API.md - der muss existieren, weil in
# einem read-only gemounteten Verzeichnis kein Mountpoint angelegt werden kann.
- ./API.md:/usr/share/nginx/html/API.md:ro
# Setzt office.html als Einstiegsseite (sonst 403 auf der Basis-URL) und liefert
# /config.js aus. Als Template gemountet, damit das nginx-Image beim Start
# MONITOR_API_BASE per envsubst einsetzt (Ausgabe: /etc/nginx/conf.d/default.conf).
- ./docker/nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro
environment:
# Wird in docker/nginx/default.conf.template eingesetzt und als /config.js an den
# Browser ausgeliefert. Leer = gleicher Origin (relatives /api/v1).
MONITOR_API_BASE: ${MONITOR_API_BASE:-}
# Speist nur den kosmetischen Reload-Countdown im Header (Issue #12) - gleicher
# Default wie beim sync-Service oben, damit die Anzeige zur tatsaechlichen
# Sync-Frequenz passt, wenn MONITOR_SYNC_INTERVAL nicht gesetzt ist.
MONITOR_SYNC_INTERVAL: ${MONITOR_SYNC_INTERVAL:-600}
ports:
- "8090:80"
# Fuehrt die Node-basierten Frontend-Tests (tests/frontend/) aus. Node/npm sind nur im
# Container noetig, nicht auf dem Host (analog zum api-Service mit PHP/Composer).
# Node >= 21 noetig: das Test-Skript in package.json uebergibt ein Glob-Muster an
# "node --test", das aeltere Versionen als Dateinamen lesen und nicht aufloesen.
frontend:
image: node:24-alpine
working_dir: /app
volumes:
- .:/app
command: npm test