Files
dotcom-arcade/docker-compose.yml
T
Darkraise 3ba60aedf9 refactor(docker): build and ship a single API + UI image
Collapse the separate backend (Node) and frontend (nginx) images into
one image where the Express backend serves the built React app in
addition to the API and game content, so deployment is a single
container and one origin.

- Add a root Dockerfile with frontend-builder + backend-builder stages
  producing one runtime image; remove backend/ and frontend/ Dockerfiles,
  the nginx config, and the frontend entrypoint
- Serve the frontend dist and SPA fallback from Express (config.serveFrontend
  / FRONTEND_DIST_PATH), before auth/maintenance so the shell always loads and
  before the API 404 so client routes resolve to index.html
- Port the SPA-oriented CSP from nginx into Helmet, gated on serveFrontend;
  keep the strict API-only policy otherwise
- Reduce both compose files to a single flashpoint-web service on WEB_PORT
  -> 3100; drop API_PORT, BACKEND_HOST, BACKEND_PORT
- Build one image in the release workflow instead of a two-service matrix
- Update .env.example, README, workflow README, and env var docs

Also add .gitattributes (*.sh eol=lf) and normalize the entrypoint line
endings — without it, Windows checkouts bake CRLF into the image and the
shebang fails with "no such file or directory".
2026-08-11 16:02:06 +07:00

86 lines
3.0 KiB
YAML

# =============================================================================
# Flashpoint Web - Production Docker Compose
# =============================================================================
# Single image serving both the API and the web UI.
# Uses a pre-built image from the container registry.
#
# Usage:
# 1. Copy .env.example to .env and configure required variables
# 2. docker compose up -d
#
# For local development with image building, use:
# docker compose -f docker-compose.dev.yml up -d --build
#
# User permissions:
# Set PUID/PGID to match your host user (run 'id -u' and 'id -g' to find)
# This ensures bind-mounted volumes have correct ownership
# =============================================================================
services:
flashpoint-web:
image: darkraise/flashpoint-web:${IMAGE_TAG:-latest}
container_name: flashpoint-web
restart: unless-stopped
ports:
# Serves both the UI and the API (/api, /game-proxy, /game-zip) on one port
- "${WEB_PORT:-80}:3100"
volumes:
- ${FLASHPOINT_HOST_PATH:?FLASHPOINT_HOST_PATH is required}:/data/flashpoint:ro
- ${DATA_PATH:-./data}:/app/data
- ${LOGS_PATH:-./logs}:/app/logs
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- NODE_ENV=production
- SERVE_FRONTEND=true
- TZ=${TZ:-UTC}
- DOMAIN=${DOMAIN:-http://localhost:${WEB_PORT:-80}}
- LOG_LEVEL=${LOG_LEVEL:-info}
- JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required}
- ENABLE_LOCAL_DB_COPY=${ENABLE_LOCAL_DB_COPY:-false}
- SQLITE_CACHE_SIZE=${SQLITE_CACHE_SIZE:--64000}
- SQLITE_MMAP_SIZE=${SQLITE_MMAP_SIZE:-268435456}
- ENABLE_CACHE_PREWARM=${ENABLE_CACHE_PREWARM:-true}
# Auth & rate limiting
- JWT_EXPIRES_IN=${JWT_EXPIRES_IN:-1h}
- BCRYPT_SALT_ROUNDS=${BCRYPT_SALT_ROUNDS:-10}
- RATE_LIMIT_WINDOW_MS=${RATE_LIMIT_WINDOW_MS:-60000}
- RATE_LIMIT_MAX_REQUESTS=${RATE_LIMIT_MAX_REQUESTS:-100}
# Application
- HOME_RECENT_HOURS=${HOME_RECENT_HOURS:-24}
- ENABLE_CGI=${ENABLE_CGI:-false}
# OpenTelemetry
- OTEL_ENABLED=${OTEL_ENABLED:-false}
- OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT:-}
- OTEL_API_KEY=${OTEL_API_KEY:-}
- OTEL_SERVICE_NAME=${OTEL_SERVICE_NAME:-flashpoint-web}
- OTEL_TRACES_ENABLED=${OTEL_TRACES_ENABLED:-true}
- OTEL_METRICS_ENABLED=${OTEL_METRICS_ENABLED:-true}
- OTEL_METRICS_EXPORT_INTERVAL=${OTEL_METRICS_EXPORT_INTERVAL:-60000}
- OTEL_LOG_LEVEL=${OTEL_LOG_LEVEL:-info}
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost:3100/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
deploy:
resources:
limits:
cpus: "2.0"
memory: 2G
reservations:
cpus: "0.5"
memory: 512M
networks:
- flashpoint-network
networks:
flashpoint-network:
driver: bridge