networks: ems-internal: driver: bridge services: # ── Traefik ───────────────────────────────────────────────────────────────── # TLS termination + mTLS on port 9443. # Perimeter firewall forwards external TCP 9443 → Synology:9443. # Only connections presenting a client cert signed by /certs/ca.crt are forwarded. traefik: image: traefik:v3.1 restart: unless-stopped ports: - "9443:9443" # HTTPS + mTLS — open on perimeter firewall volumes: - ./infra/traefik/traefik.yml:/etc/traefik/traefik.yml:ro - ./infra/traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro # Certs: ca.crt, server.crt, server.key (generated by infra/ca/ scripts) - /etc/ems/certs:/certs:ro networks: - ems-internal depends_on: - ems # ── EMS ───────────────────────────────────────────────────────────────────── # Web UI is NOT exposed externally — Traefik proxies to :9099 internally. # Prometheus metrics on :9101 stay LAN-accessible for the existing scrape job. ems: build: . restart: unless-stopped ports: # Metrics — Prometheus scrape from LAN (192.168.0.23:9090) # Do NOT route through Traefik (no auth on metrics endpoint by design). - "9101:9101" volumes: # Config (read-only) — must be chmod 600 - /etc/ems:/etc/ems:ro # Persistent state: trip goals, sessions, auth tokens - /var/lib/ems:/var/lib/ems # Heartbeat file - /run/ems:/run/ems networks: - ems-internal environment: - TZ=Europe/Berlin # Health check using the unauthenticated /health endpoint healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:9099/health"] interval: 30s timeout: 5s retries: 3