Deployment Topology
Status
Active
Date
2026-04-28
Owners
- Platform Backend
Last Verified Commit
Use git log -- <this file> for current last-touch history; this field is intentionally not pinned to a static hash so it does not become stale after unrelated commits.
Local Stack Source
The canonical local deployment topology is the Compose stack in:
servers_v2/docker-compose.yml
The stack is contract-tested by:
servers_v2/tests/test_compose_contract.py
Infrastructure Services
| Service | Purpose | Local Port |
|---|---|---|
postgres | shared PostgreSQL runtime | 5433 |
redis | shared Redis runtime and stream transport | 6381 |
migrate | one-shot migration bootstrap | none |
Application Services
| Service | Local Port | Health Mode |
|---|---|---|
gateway | 8080 | /health |
player_service | 8010 | /health |
wallet_service | 8011 | /health |
game_service | 8012 | /health |
rolling_service | 8013 | /health |
promotion_service | 8014 | /health |
agent_service | 8015 | /health |
admin_service | 8016 | /health |
recon_service | 8017 | /health |
Base compose publishes gateway on the host-facing default bind because it is
the local player/API entrypoint. Every other published application and
infrastructure port is pinned to 127.0.0.1, including admin_service and
agent_service; expose those services outside the host only through an
external ingress boundary such as VPN/LB/firewall/TLS plus auth, rate limits,
and allow-lists. The compose file is a local verification topology, not a full
public production network boundary.
Worker Services
| Worker | Purpose | Health Mode |
|---|---|---|
wallet_worker | wallet outbox publishing, outbox lag gauges, outbox cleanup, orphan authorization sweeper, hourly ledger-vs-balance reconciliation | heartbeat file + readiness |
rolling_worker | wallet-event consumer, outbox, outbox cleanup, expiry, completion retry | heartbeat file + loop freshness |
promotion_worker | wallet-event consumer, settlement scheduler, saga recovery | heartbeat file + scheduler readiness |
agent_worker | agent outbox publishing, outbox cleanup, coupon wallet saga dispatch | heartbeat file |
admin_worker | scheduled admin jobs | heartbeat file + scheduler job health |
recon_worker | Pushbullet/Telegram/recon loops | heartbeat file + loop freshness |
player_worker | player outbox publishing (ENABLE_OUTBOX_POLLER=true); player_service API has it disabled and depends_on: player_worker to guarantee the worker is healthy before traffic | heartbeat file |
Current Split-Worker Rule
Services with dedicated background work run that work in a standalone worker so APIs can scale independently.
There are currently no exceptions. Compose keeps API-owned process flags off
for worker-owned loops (ENABLE_*: "false" on APIs) and enables the matching
flags on worker containers. servers_v2/tests/test_compose_contract.py locks
that split so API horizontal scaling cannot duplicate settlement, saga,
cleanup, scheduler, or stream-consumer loops.
Environment Files
Local stack usage is designed around:
servers_v2/.env.compose.exampleservers_v2/.env.compose.local
Sensitive values are intentionally env-driven and must not be committed into the Compose file.
Multi-Brand Mode
Per ADR-009, every brand runs on the same Compose stack:
- one
postgres, oneredis, one set of application services and workers - brand identity is resolved per request from the request domain (or, for game callbacks, from the namespaced outbound account); no per-brand process duplication
- a
MULTI_BRAND_ENFORCEMENTenv var (off/observe/enforce) controls Gateway's JWT-vs-domain brand mismatch behavior; the repository compose default is nowenforce, withobservereserved for explicit soak/rehearsal overrides
Recommended Local Validation
Use the local-stack runbook:
docs/runbooks/local-docker/local-docker-full-stack.md