name: ${COMPOSE_PROJECT_NAME:-zarinpal_analytics} x-logging: &bounded-logging driver: json-file options: max-size: 10m max-file: "3" x-backend-runtime: &backend-runtime image: zarinpal-analytics-backend:${IMAGE_TAG:-local} build: context: ./backend dockerfile: ../docker/backend.Dockerfile env_file: - ./backend/.env environment: DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable REDIS_ADDR: redis:6379 REDIS_PASSWORD: ${REDIS_PASSWORD} init: true restart: unless-stopped security_opt: - no-new-privileges:true logging: *bounded-logging services: postgres: image: postgres:18.4-alpine3.24 restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql networks: - data healthcheck: test: ["CMD-SHELL", "pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\""] interval: 10s timeout: 3s retries: 5 start_period: 30s shm_size: 128mb mem_limit: 512m cpus: 0.75 pids_limit: 200 logging: *bounded-logging redis: image: redis:7.4.10-alpine3.21 restart: unless-stopped environment: REDIS_PASSWORD: ${REDIS_PASSWORD} command: - /bin/sh - -ec - | chown redis:redis /data exec /usr/bin/setpriv --reuid redis --regid redis --clear-groups \ /bin/sh -ec ' umask 077 printf "appendonly yes\nappendfsync everysec\nmaxmemory 192mb\nmaxmemory-policy noeviction\nrequirepass %s\n" "$$REDIS_PASSWORD" > /data/redis.conf exec redis-server /data/redis.conf ' volumes: - redis_data:/data networks: - data healthcheck: test: ["CMD-SHELL", "REDISCLI_AUTH=\"$$REDIS_PASSWORD\" redis-cli ping | grep -q PONG"] interval: 10s timeout: 3s retries: 5 start_period: 10s mem_limit: 256m cpus: 0.25 pids_limit: 100 security_opt: - no-new-privileges:true logging: *bounded-logging backend: <<: *backend-runtime command: ["/app/api"] depends_on: postgres: condition: service_healthy redis: condition: service_healthy expose: - "8080" networks: data: caddy_proxy: aliases: - zarinpal-backend healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/healthz"] interval: 10s timeout: 3s retries: 5 start_period: 30s stop_grace_period: 20s mem_limit: 256m cpus: 0.50 pids_limit: 150 worker: <<: *backend-runtime command: ["/app/worker"] depends_on: postgres: condition: service_healthy redis: condition: service_healthy networks: - data - egress dns: - 1.1.1.1 - 8.8.8.8 healthcheck: test: ["CMD-SHELL", "kill -0 1"] interval: 10s timeout: 3s retries: 5 start_period: 30s stop_grace_period: 70s mem_limit: 512m cpus: 0.75 pids_limit: 200 frontend: image: zarinpal-analytics-frontend:${IMAGE_TAG:-local} build: context: ./frontend dockerfile: ../docker/frontend.Dockerfile init: true restart: unless-stopped expose: - "8080" networks: caddy_proxy: aliases: - zarinpal-frontend healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/healthz"] interval: 10s timeout: 3s retries: 5 start_period: 15s security_opt: - no-new-privileges:true mem_limit: 96m cpus: 0.25 pids_limit: 100 logging: *bounded-logging migrate: <<: *backend-runtime command: ["/app/migrate"] restart: "no" depends_on: postgres: condition: service_healthy networks: - data profiles: ["tools"] importer: <<: *backend-runtime command: ["/app/importer"] restart: "no" depends_on: postgres: condition: service_healthy volumes: - ${IMPORT_FILE:-/srv/zarinpal-data/challenge_data.csv.gz}:/import/challenge_data.csv.gz:ro networks: - data profiles: ["import"] admin: <<: *backend-runtime command: ["/app/admin"] restart: "no" depends_on: postgres: condition: service_healthy networks: - data profiles: ["tools"] calendar: <<: *backend-runtime entrypoint: ["/app/calendar"] command: - -action - bootstrap - -file - /calendar/reviewed.json - -current-jalali-year - "${CALENDAR_CURRENT_JALALI_YEAR:-0}" - -reviewer - "${CALENDAR_REVIEWER:-}" - -actor - "${CALENDAR_OPERATOR:-}" restart: "no" depends_on: postgres: condition: service_healthy volumes: - ${CALENDAR_FILE:-/srv/zarinpal-data/reviewed-calendar.json}:/calendar/reviewed.json:ro networks: - data profiles: ["tools"] networks: data: internal: true egress: driver: bridge caddy_proxy: external: true name: ${CADDY_NETWORK:-caddy_proxy} volumes: postgres_data: redis_data: