# ============================================================================= # FlintTrade Docker Compose — Production Deployment # ============================================================================= # # Usage: # docker compose up -d # start the app services (default profile) # docker compose up -d --build # rebuild and start the app services # docker compose --profile monitoring up -d # app + observability stack # docker compose logs -f flinttrade # follow FlintTrade logs # docker compose down # stop all services # # Profiles: # default -> flinttrade (backend), terminal (one-shot UI build), nginx (UI origin) # monitoring -> Uptime Kuma, GlitchTip (+ its Postgres/Redis), VictoriaLogs, Netdata. # The GlitchTip services need real GLITCHTIP_SECRET_KEY / # GLITCHTIP_DB_PASSWORD values in .env — the baked-in defaults are # config-load placeholders only, never production credentials. # # Architecture: # Nginx -> React static files + reverse proxy to backend services. In Docker, # Nginx is the UI origin: http://localhost:8080 by default (override the host # ports with FLINTTRADE_HTTP_PORT / FLINTTRADE_HTTPS_PORT). # FlintTrade (5100) -> Platform backend (strategy engine, screener, AI). The # image does not contain the built terminal, so port 5100 is API-only in # Docker — the UI is served by Nginx from the terminal build volume. # Monitoring stack (profile: monitoring) -> Uptime Kuma, GlitchTip, # VictoriaLogs, Netdata # # All internal services bind to 127.0.0.1 only. Nginx is the sole entry point. # A .env file is optional: the app services start without one. # # Multi-arch builds: # All base images (python:3.14-slim-trixie, node:22-alpine, nginx:alpine, etc.) support # linux/amd64 and linux/arm64. To build the FlintTrade image for multiple platforms: # docker buildx build --platform linux/amd64,linux/arm64 -t flinttrade:latest . # ============================================================================= services: # --------------------------------------------------------------------------- flinttrade: build: context: . dockerfile: Dockerfile image: flinttrade:latest platform: linux/amd64 container_name: flinttrade-backend volumes: - flinttrade_data:/data/flinttrade - ${FLINTTRADE_HOME:-~/.flinttrade}:/home/flinttrade/.flinttrade - flinttrade_strategies:/data/flinttrade/strategies - flinttrade_logs:/data/flinttrade/logs ports: - "127.0.0.1:5100:5100" # .env is gitignored and absent from a fresh clone; required: false keeps # `docker compose up` working without it (Compose v2.24+). env_file: - path: .env required: false environment: - FLINTTRADE_DEV=0 - FLINTTRADE_BACKEND_PORT=5100 - OPENBLAS_NUM_THREADS=2 - OMP_NUM_THREADS=2 - GUNICORN_WORKERS=1 - GUNICORN_TIMEOUT=120 - LOG_LEVEL=info restart: unless-stopped read_only: true tmpfs: - /tmp:size=100M security_opt: - "no-new-privileges:true" healthcheck: test: ["CMD", "curl", "-sf", "http://localhost:5100/api/v1/ping"] interval: 30s timeout: 10s retries: 3 start_period: 15s logging: driver: json-file options: max-size: "50m" max-file: "5" networks: - flinttrade # --------------------------------------------------------------------------- # Terminal — Build React static files (builder only, no runtime container) # Nginx serves the built dist/ directory. # # This is a pnpm workspace (root pnpm-lock.yaml, no package-lock.json) and the # terminal imports @flinttrade/design-system via a file: path that lives # outside the terminal package, so the whole repo must be mounted and built # through the workspace. The commands mirror the node-core-tests job in # .github/workflows/test.yml: corepack enable, then a frozen-lockfile install, # then build the terminal package. Vite writes to the default dist/ inside the # terminal package, which is the path Nginx mounts via terminal_dist. # --------------------------------------------------------------------------- terminal: image: node:22-alpine platform: linux/amd64 container_name: flinttrade-terminal-builder working_dir: /workspace volumes: - .:/workspace - terminal_dist:/workspace/packages/apps/terminal/dist - terminal_node_modules:/workspace/node_modules # Corepack is no longer distributed with Node.js from v25.0.0 onwards, so # 'corepack enable' cannot be relied on once this image moves off the 22 # line. Fall through to a version-matched pnpm and finally to npx with the # pinned version - the same chain as infra/scripts/setup.sh. # '$$' escapes Compose's own interpolation so the shell receives '$PNPM'. command: > sh -c "if command -v corepack >/dev/null 2>&1; then corepack enable; fi; if command -v pnpm >/dev/null 2>&1; then PNPM='pnpm'; else PNPM='npx --yes pnpm@10.34.5'; fi; $$PNPM install --frozen-lockfile && $$PNPM --dir packages/apps/terminal run build" environment: - NODE_ENV=production restart: "no" logging: driver: json-file options: max-size: "10m" max-file: "3" networks: - flinttrade # --------------------------------------------------------------------------- # Nginx — Reverse proxy and static file server (sole public entry point) # --------------------------------------------------------------------------- nginx: image: nginx:1.30.5-alpine@sha256:0985e772fb9f729e6fa0980da05fca5d9c468e870eed43071545afa9d2e27d94 platform: linux/amd64 container_name: flinttrade-nginx # High-port defaults so a dev machine with a busy :80/:443 still comes up. # Production hosts that want the classic ports set FLINTTRADE_HTTP_PORT=80 # and FLINTTRADE_HTTPS_PORT=443 in .env. ports: - "${FLINTTRADE_HTTP_PORT:-8080}:80" - "${FLINTTRADE_HTTPS_PORT:-8443}:443" volumes: - ./infra/nginx/flinttrade.conf:/etc/nginx/conf.d/default.conf:ro - terminal_dist:/var/www/flinttrade/dist:ro - nginx_logs:/var/log/nginx # Uncomment when SSL certificates are provisioned: # - /etc/letsencrypt:/etc/letsencrypt:ro depends_on: terminal: condition: service_completed_successfully flinttrade: condition: service_healthy restart: unless-stopped healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:80/"] interval: 30s timeout: 5s retries: 3 logging: driver: json-file options: max-size: "50m" max-file: "5" networks: - flinttrade # --------------------------------------------------------------------------- # Uptime Kuma — Uptime monitoring dashboard # Internal only (proxied via Nginx at /monitoring/) # --------------------------------------------------------------------------- uptime-kuma: image: louislam/uptime-kuma:2.5.6@sha256:9912da7d7d9b6ddc0c2d904beb84d24689c1ed9c33b81419eb9008663a92a329 platform: linux/amd64 container_name: flinttrade-uptime-kuma profiles: ["monitoring"] ports: - "127.0.0.1:3001:3001" volumes: - uptime_kuma_data:/app/data restart: unless-stopped logging: driver: json-file options: max-size: "20m" max-file: "3" networks: - flinttrade # --------------------------------------------------------------------------- # Glitchtip — Error tracking (Sentry-compatible, MIT licence) # Internal only (proxied via Nginx at /errors/) # Requires PostgreSQL for storage + Celery worker for async processing # --------------------------------------------------------------------------- glitchtip-redis: image: redis:8.10.2-alpine@sha256:3811787313eba226a2ef38658c6ccb91cd5e110edc89c37767de373120a0e5a0 platform: linux/amd64 container_name: flinttrade-glitchtip-redis profiles: ["monitoring"] restart: unless-stopped logging: driver: json-file options: max-size: "10m" max-file: "3" networks: - flinttrade glitchtip: image: glitchtip/glitchtip:6.2.6@sha256:a3d8eb1b36c1e1603d55ab32711ea3dd8115874742a781a57391a62a16e0dff6 platform: linux/amd64 container_name: flinttrade-glitchtip profiles: ["monitoring"] ports: - "127.0.0.1:8000:8000" depends_on: glitchtip-db: condition: service_healthy # `:-` (not `:?`) — a `:?` here aborts config load for the WHOLE compose # file, so even `docker compose up flinttrade` failed on an optional # sidecar. The dev-only defaults exist purely so config loads without a # .env; set real values before running the monitoring profile. environment: DATABASE_URL: "postgres://glitchtip:${GLITCHTIP_DB_PASSWORD:-flinttrade-dev-only-change-me}@glitchtip-db:5432/glitchtip" SECRET_KEY: "${GLITCHTIP_SECRET_KEY:-flinttrade-dev-only-change-me}" CELERY_BROKER_URL: "redis://glitchtip-redis:6379/0" PORT: "8000" GLITCHTIP_DOMAIN: "http://127.0.0.1:8000" restart: unless-stopped logging: driver: json-file options: max-size: "20m" max-file: "3" networks: - flinttrade glitchtip-worker: image: glitchtip/glitchtip:6.2.6@sha256:a3d8eb1b36c1e1603d55ab32711ea3dd8115874742a781a57391a62a16e0dff6 platform: linux/amd64 container_name: flinttrade-glitchtip-worker profiles: ["monitoring"] command: bin/run-celery-with-beat.sh depends_on: glitchtip-db: condition: service_healthy glitchtip-redis: condition: service_started environment: DATABASE_URL: "postgres://glitchtip:${GLITCHTIP_DB_PASSWORD:-flinttrade-dev-only-change-me}@glitchtip-db:5432/glitchtip" SECRET_KEY: "${GLITCHTIP_SECRET_KEY:-flinttrade-dev-only-change-me}" CELERY_BROKER_URL: "redis://glitchtip-redis:6379/0" restart: unless-stopped logging: driver: json-file options: max-size: "20m" max-file: "3" networks: - flinttrade glitchtip-db: image: postgres:16.15-alpine@sha256:721873c34ceb9f8d8fc265984940dc982404c105f19ad51be9fdc5970a6080ea platform: linux/amd64 container_name: flinttrade-glitchtip-db profiles: ["monitoring"] volumes: - glitchtip_pg:/var/lib/postgresql/data environment: POSTGRES_USER: glitchtip POSTGRES_PASSWORD: "${GLITCHTIP_DB_PASSWORD:-flinttrade-dev-only-change-me}" POSTGRES_DB: glitchtip healthcheck: test: ["CMD-SHELL", "pg_isready -U glitchtip"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped logging: driver: json-file options: max-size: "10m" max-file: "3" networks: - flinttrade # --------------------------------------------------------------------------- # VictoriaLogs — Log aggregation and search # Internal only (proxied via Nginx at /logs/) # --------------------------------------------------------------------------- victorialogs: image: victoriametrics/victoria-logs:v1.53.0@sha256:251121fa882af99b95ba0c230a4a2f412ea602d2698c64a96c58dc9842bb755d platform: linux/amd64 container_name: flinttrade-victorialogs profiles: ["monitoring"] ports: - "127.0.0.1:9090:9090" volumes: - victorialogs_data:/victoria-logs-data command: - "-storageDataPath=/victoria-logs-data" - "-httpListenAddr=:9090" restart: unless-stopped logging: driver: json-file options: max-size: "20m" max-file: "3" networks: - flinttrade # --------------------------------------------------------------------------- # Netdata — Real-time system performance monitoring # Internal only (proxied via Nginx at /metrics/) # --------------------------------------------------------------------------- netdata: image: netdata/netdata:v2.12.1@sha256:5cfa05a5fa5686b98e0c70e884f00685fb3c662e9c5d727cf46c96d8e37b7818 platform: linux/amd64 container_name: flinttrade-netdata profiles: ["monitoring"] ports: - "127.0.0.1:19999:19999" volumes: - netdata_config:/etc/netdata - netdata_lib:/var/lib/netdata - netdata_cache:/var/cache/netdata - /proc:/host/proc:ro - /sys:/host/sys:ro - /etc/os-release:/host/etc/os-release:ro cap_add: - SYS_PTRACE security_opt: - apparmor:unconfined restart: unless-stopped logging: driver: json-file options: max-size: "20m" max-file: "3" networks: - flinttrade # ============================================================================= # Named volumes — persistent data across container restarts # ============================================================================= volumes: # Core application flinttrade_data: name: flinttrade_platform_data flinttrade_strategies: name: flinttrade_strategies flinttrade_logs: name: flinttrade_logs # Terminal build terminal_dist: name: flinttrade_terminal_dist terminal_node_modules: name: flinttrade_terminal_node_modules # Nginx nginx_logs: name: flinttrade_nginx_logs # Monitoring uptime_kuma_data: name: flinttrade_uptime_kuma_data glitchtip_pg: name: flinttrade_glitchtip_pg victorialogs_data: name: flinttrade_victorialogs_data netdata_config: name: flinttrade_netdata_config netdata_lib: name: flinttrade_netdata_lib netdata_cache: name: flinttrade_netdata_cache # ============================================================================= # Internal network — all services communicate over this bridge # ============================================================================= networks: flinttrade: name: flinttrade driver: bridge