name: rf4-spotter x-logging: &default-logging driver: json-file options: max-size: "10m" max-file: "5" services: proxy: image: caddy:2.10.2-alpine restart: unless-stopped environment: SITE_DOMAIN: ${SITE_DOMAIN:?Set SITE_DOMAIN} FILES_DOMAIN: ${FILES_DOMAIN:?Set FILES_DOMAIN} ACME_EMAIL: ${ACME_EMAIL:?Set ACME_EMAIL} ADMIN_BASIC_USER: ${ADMIN_BASIC_USER:?Set ADMIN_BASIC_USER} ADMIN_BASIC_PASSWORD_HASH: ${ADMIN_BASIC_PASSWORD_HASH:?Set ADMIN_BASIC_PASSWORD_HASH} ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./deploy/Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: web: condition: service_healthy networks: [edge, backend] security_opt: [no-new-privileges:true] logging: *default-logging deploy: resources: limits: {cpus: "0.50", memory: 256M} db: image: postgres:17-alpine restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB:-rf4_spotter} POSTGRES_USER: ${POSTGRES_USER:-rf4} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-rf4} -d ${POSTGRES_DB:-rf4_spotter}"] interval: 10s timeout: 5s retries: 10 networks: [backend] security_opt: [no-new-privileges:true] logging: *default-logging deploy: resources: limits: {cpus: "1.00", memory: 1G} minio: image: minio/minio:RELEASE.2025-07-23T15-54-02Z command: server /data restart: unless-stopped environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:?Set MINIO_ROOT_USER} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?Set MINIO_ROOT_PASSWORD} volumes: - minio_data:/data healthcheck: test: ["CMD", "curl", "-fsS", "http://localhost:9000/minio/health/live"] interval: 10s timeout: 5s retries: 10 networks: [backend] security_opt: [no-new-privileges:true] logging: *default-logging deploy: resources: limits: {cpus: "0.75", memory: 1G} minio-init: image: minio/mc:RELEASE.2025-07-21T05-28-08Z restart: "no" entrypoint: ["/bin/sh", "-c"] command: - >- test "$$MINIO_ROOT_USER" != "$$S3_ACCESS_KEY" && test "$$MINIO_ROOT_PASSWORD" != "$$S3_SECRET_KEY" && mc alias set local http://minio:9000 "$$MINIO_ROOT_USER" "$$MINIO_ROOT_PASSWORD" >/dev/null && (mc admin user info local "$$S3_ACCESS_KEY" >/dev/null 2>&1 || mc admin user add local "$$S3_ACCESS_KEY" "$$S3_SECRET_KEY") && mc admin policy attach local readwrite --user "$$S3_ACCESS_KEY" >/dev/null environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:?Set MINIO_ROOT_USER} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?Set MINIO_ROOT_PASSWORD} S3_ACCESS_KEY: ${S3_ACCESS_KEY:?Set S3_ACCESS_KEY} S3_SECRET_KEY: ${S3_SECRET_KEY:?Set S3_SECRET_KEY} depends_on: minio: {condition: service_healthy} networks: [backend] security_opt: [no-new-privileges:true] logging: *default-logging api: build: context: . dockerfile: apps/api/Dockerfile restart: unless-stopped environment: DEPLOYMENT_ENVIRONMENT: production DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL} ADMIN_TOKEN: ${ADMIN_TOKEN:?Set ADMIN_TOKEN} CORS_ORIGINS: '["https://${SITE_DOMAIN:?Set SITE_DOMAIN}"]' S3_ENDPOINT_URL: http://minio:9000 S3_PUBLIC_ENDPOINT_URL: https://${FILES_DOMAIN:?Set FILES_DOMAIN} S3_ACCESS_KEY: ${S3_ACCESS_KEY:?Set S3_ACCESS_KEY} S3_SECRET_KEY: ${S3_SECRET_KEY:?Set S3_SECRET_KEY} S3_BUCKET: ${S3_BUCKET:-catch-screenshots} OFFICIAL_RECORDS_URL: ${OFFICIAL_RECORDS_URL:-https://rf4game.de/records/region/RU/} OFFICIAL_RECORDS_REGION: ${OFFICIAL_RECORDS_REGION:-RU} OFFICIAL_RECORDS_CATEGORY: ${OFFICIAL_RECORDS_CATEGORY:-records} OFFICIAL_IMPORT_REQUIRED: ${OFFICIAL_IMPORT_REQUIRED:-false} SEED_DEMO_DATA: "false" IMPORT_INTERVAL_SECONDS: ${IMPORT_INTERVAL_SECONDS:-3600} RATE_LIMIT_SECRET: ${RATE_LIMIT_SECRET:?Set RATE_LIMIT_SECRET} RETENTION_SUBMISSION_DAYS: ${RETENTION_SUBMISSION_DAYS:-1} RETENTION_UNREVIEWED_DAYS: ${RETENTION_UNREVIEWED_DAYS:-30} RETENTION_APPROVED_PERSONAL_DAYS: ${RETENTION_APPROVED_PERSONAL_DAYS:-180} RETENTION_STAGING_DAYS: ${RETENTION_STAGING_DAYS:-90} RETENTION_AUDIT_DAYS: ${RETENTION_AUDIT_DAYS:-365} RETENTION_PUBLISHED_PAYLOAD_DAYS: ${RETENTION_PUBLISHED_PAYLOAD_DAYS:-365} LOG_LEVEL: ${LOG_LEVEL:-INFO} depends_on: db: {condition: service_healthy} minio: {condition: service_healthy} minio-init: {condition: service_completed_successfully} healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/ready')"] interval: 10s timeout: 5s retries: 12 networks: [backend, edge] security_opt: [no-new-privileges:true] logging: *default-logging deploy: resources: limits: {cpus: "1.00", memory: 1G} web: build: context: ./apps/web args: PUBLIC_API_URL: https://${SITE_DOMAIN:?Set SITE_DOMAIN} restart: unless-stopped environment: PUBLIC_API_URL: https://${SITE_DOMAIN:?Set SITE_DOMAIN} API_INTERNAL_URL: http://api:8000 depends_on: api: {condition: service_healthy} healthcheck: test: ["CMD", "node", "-e", "fetch('http://localhost:4321').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"] interval: 10s timeout: 5s retries: 10 networks: [backend] security_opt: [no-new-privileges:true] logging: *default-logging deploy: resources: limits: {cpus: "0.75", memory: 512M} storage-tool: image: minio/mc:RELEASE.2025-07-21T05-28-08Z profiles: [tools] environment: S3_ACCESS_KEY: ${S3_ACCESS_KEY:?Set S3_ACCESS_KEY} S3_SECRET_KEY: ${S3_SECRET_KEY:?Set S3_SECRET_KEY} S3_BUCKET: ${S3_BUCKET:-catch-screenshots} volumes: - ${BACKUP_DIRECTORY:-./backups}:/backup networks: [backend] security_opt: [no-new-privileges:true] logging: *default-logging networks: edge: backend: internal: true volumes: postgres_data: minio_data: caddy_data: caddy_config: