services: app: build: context: . args: APP_VERSION: ${APP_VERSION:-4.0.1} container_name: goodwalk_svelte_app environment: APP_VERSION: ${APP_VERSION:-4.0.1} DATABASE_URL: postgresql://${POSTGRES_USER:-goodwalk}:${POSTGRES_PASSWORD_URLENCODED:-goodwalk}@db:5432/${POSTGRES_DB:-goodwalk} NODE_ENV: production PORT: 3000 ENABLE_GENERAL_ENQUIRIES: ${ENABLE_GENERAL_ENQUIRIES:-false} PUBLIC_ENABLE_MOBILE_CTA_BUTTON: ${PUBLIC_ENABLE_MOBILE_CTA_BUTTON:-false} PUBLIC_ENABLE_ENHANCED_CONTENT_IMAGES: ${PUBLIC_ENABLE_ENHANCED_CONTENT_IMAGES:-false} TZ: ${TZ:-Pacific/Auckland} depends_on: - db expose: - '3000' restart: unless-stopped networks: - default - webnet mail-api: build: context: ./mail-api args: APP_VERSION: ${APP_VERSION:-4.0.1} container_name: goodwalk_svelte_mail_api depends_on: - db environment: APP_VERSION: ${APP_VERSION:-4.0.1} RESEND_API_KEY: ${RESEND_API_KEY} OWNER_EMAIL: ${OWNER_EMAIL} SECONDARY_CP_EMAIL: ${SECONDARY_CP_EMAIL:-} SECONDARY_CP_EMAILS: ${SECONDARY_CP_EMAILS:-} OWNER_BCC: ${OWNER_BCC:-} CLIENT_BCC: ${CLIENT_BCC:-} FROM_EMAIL: ${FROM_EMAIL:-GoodWalk } REPLY_TO: ${REPLY_TO:-aless@goodwalk.co.nz} ENABLE_GENERAL_ENQUIRIES: ${ENABLE_GENERAL_ENQUIRIES:-false} DATA_DIR: ${MAIL_API_DATA_DIR:-/app/data} # Shared postgres for admin state (client_profiles, allowed_emails, drafts). # When unset the mail-api falls back to JSON files under DATA_DIR. DATABASE_URL: postgresql://${POSTGRES_USER:-goodwalk}:${POSTGRES_PASSWORD_URLENCODED:-goodwalk}@db:5432/${POSTGRES_DB:-goodwalk} ADMIN_DATA_SEED_FROM_JSON: ${ADMIN_DATA_SEED_FROM_JSON:-auto} FORM_MIN_SECONDS: ${FORM_MIN_SECONDS:-4} FORM_MAX_SECONDS: ${FORM_MAX_SECONDS:-7200} RATE_LIMIT_WINDOW_SECONDS: ${RATE_LIMIT_WINDOW_SECONDS:-900} RATE_LIMIT_MAX_PER_IP: ${RATE_LIMIT_MAX_PER_IP:-5} RATE_LIMIT_MAX_PER_EMAIL: ${RATE_LIMIT_MAX_PER_EMAIL:-3} RATE_LIMIT_MIN_INTERVAL_SECONDS: ${RATE_LIMIT_MIN_INTERVAL_SECONDS:-20} CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-} TRUSTED_HOSTS: ${TRUSTED_HOSTS:-} MAX_REQUEST_BODY_BYTES: ${MAX_REQUEST_BODY_BYTES:-} PYTHONUNBUFFERED: '1' TZ: ${TZ:-Pacific/Auckland} expose: - '8000' volumes: - mail_api_data:${MAIL_API_DATA_DIR:-/app/data} restart: unless-stopped healthcheck: # Hits /health via the container's own loopback so TrustedHostMiddleware # (allowed_hosts) does not need to know about the bridge-network IP. test: ["CMD-SHELL", "python -c \"import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).status == 200 else 1)\""] interval: 30s timeout: 5s retries: 3 start_period: 20s networks: - default - webnet db: image: postgres:16-alpine container_name: goodwalk_svelte_db environment: POSTGRES_DB: ${POSTGRES_DB:-goodwalk} POSTGRES_USER: ${POSTGRES_USER:-goodwalk} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-goodwalk} volumes: - postgres_data:/var/lib/postgresql/data - ./docker/postgres/init:/docker-entrypoint-initdb.d:ro restart: unless-stopped networks: - default volumes: postgres_data: mail_api_data: networks: webnet: external: true