services: backend: &default_backend build: &default_backend_build context: . dockerfile: packages/backend/Dockerfile # these args configure private env vars for the backend and public env vars for the frontend depends_on: db: condition: service_started redis: condition: service_started frontend: condition: service_started migration: condition: service_completed_successfully restart: unless-stopped environment: &default_backend_env_vars NODE_ENV: production ADMIN_USER: ${ADMIN_USER} ADMIN_EMAIL: ${ADMIN_EMAIL} ADMIN_PASSWORD: ${ADMIN_PASSWORD} JWT_SECRET: ${JWT_SECRET} DOMAIN_NAME: ${DOMAIN_NAME} CACHE_DOMAIN: ${CACHE_DOMAIN} MEDIA_DOMAIN: ${MEDIA_DOMAIN} DONATION_URL: ${DONATION_URL} SMTP_HOST: ${SMTP_HOST} SMTP_USER: ${SMTP_USER} SMTP_PORT: ${SMTP_PORT} SMTP_PASSWORD: ${SMTP_PASSWORD} SMTP_FROM: ${SMTP_FROM} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DBNAME: ${POSTGRES_DBNAME} WEBPUSH_EMAIL: ${WEBPUSH_EMAIL} WEBPUSH_PRIVATE: ${WEBPUSH_PRIVATE} WEBPUSH_PUBLIC: ${WEBPUSH_PUBLIC} ENABLE_BSKY: ${ENABLE_BSKY} PDS_DOMAIN_NAME: ${PDS_DOMAIN_NAME} PDS_JWT_SECRET: ${PDS_JWT_SECRET} PDS_ADMIN_PASSWORD: ${PDS_ADMIN_PASSWORD} USE_WORKERS: false LOG_SQL_QUERIES: ${LOG_SQL_QUERIES:-} UPLOAD_LIMIT: ${UPLOAD_LIMIT:-} POSTS_PER_PAGE: ${POSTS_PER_PAGE:-} LOG_LEVEL: ${LOG_LEVEL:-} BLOCKLIST_URI: ${BLOCKLIST_URI:-} FRONTEND_PATH: ${FRONTEND_PATH:-} DISABLE_REQUIRE_SEND_EMAIL: ${DISABLE_REQUIRE_SEND_EMAIL:-} BLOCKED_IPS: ${BLOCKED_IPS:-} REVIEW_REGISTRATIONS: ${REVIEW_REGISTRATIONS:-} IGNORE_BLOCK_HOSTS: ${IGNORE_BLOCK_HOSTS:-} FRONTEND_LOGO: ${FRONTEND_LOGO:-} FRONTEND_API_URL: ${FRONTEND_API_URL:-} FRONTEND_MEDIA_URL: ${FRONTEND_MEDIA_URL:-} FRONTEND_CACHE_URL: ${FRONTEND_CACHE_URL:-} FRONTEND_CACHE_BACKUP_URLS: ${FRONTEND_CACHE_BACKUP_URLS:-} FRONTEND_SHORTEN_POSTS: ${FRONTEND_SHORTEN_POSTS:-} FRONTEND_DISABLE_PWA: ${FRONTEND_DISABLE_PWA:-} FRONTEND_MAINTENANCE: ${FRONTEND_MAINTENANCE:-} FRONTEND_SHORT_TITLE: ${FRONTEND_SHORT_TITLE:-} FRONTEND_LONG_TITLE: ${FRONTEND_LONG_TITLE:-} FRONTEND_DESCRIPTION: ${FRONTEND_DESCRIPTION:-} REGISTRATION_LEVEL: ${REGISTRATION_LEVEL:-} REGISTRATIONS_DISABLED_TEXT: ${REGISTRATIONS_DISABLED_TEXT:-} HIDE_BLOCKED_SERVERS: ${HIDE_BLOCKED_SERVERS:-} REGISTRATION_MINIMUM_AGE: ${REGISTRATION_MINIMUM_AGE:-} FRONTEND_FQDN_URL: https://${DOMAIN_NAME} ENABLE_RAW_OUTPUT: ${ENABLE_RAW_OUTPUT:-} deploy: mode: replicated replicas: 3 volumes: - ./packages/backend/uploads:/app/packages/backend/uploads - ./packages/backend/cache:/app/packages/backend/cache - frontend:/app/packages/frontend:ro migration: <<: *default_backend depends_on: db: condition: service_started redis: condition: service_started frontend: condition: service_started restart: no deploy: mode: replicated replicas: 1 command: "npm exec tsx migrate.ts init-container" frontend: restart: unless-stopped build: context: . dockerfile: packages/frontend/Dockerfile ports: - 80:80 - 443:443 environment: DOMAIN_NAME: ${DOMAIN_NAME} PDS_DOMAIN_NAME: ${PDS_DOMAIN_NAME} CACHE_DOMAIN: ${CACHE_DOMAIN} MEDIA_DOMAIN: ${MEDIA_DOMAIN} DONATION_URL: ${DONATION_URL} ACME_EMAIL: ${ACME_EMAIL} FRONTEND_SHORT_TITLE: ${FRONTEND_SHORT_TITLE:-} FRONTEND_LONG_TITLE: ${FRONTEND_LONG_TITLE:-} FRONTEND_DESCRIPTION: ${FRONTEND_DESCRIPTION:-} REGISTRATION_LEVEL: ${REGISTRATION_LEVEL:-} REGISTRATIONS_DISABLED_TEXT: ${REGISTRATIONS_DISABLED_TEXT:-} HIDE_BLOCKED_SERVERS: ${HIDE_BLOCKED_SERVERS:-} REGISTRATION_MINIMUM_AGE: ${REGISTRATION_MINIMUM_AGE:-} CACHE_HOST: "cache:9000" BACKEND_HOST: "wafrn-backend-1:9000 wafrn-backend-2:9000 wafrn-backend-3:9000" WEBSOCKET_HOST: "wafrn-websocket-1:9000" volumes: - "caddy:/data" - "frontend:/var/www/html/frontend" - ./packages/backend/uploads:/var/www/html/uploads - ./packages/caddy:/etc/caddy/config db: image: postgres:17 restart: unless-stopped shm_size: '2gb' environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DBNAME} volumes: - dbpg:/var/lib/postgresql/data adminer: image: adminer restart: unless-stopped redis: image: redis:7.2.4 restart: unless-stopped volumes: - redis:/data pds: image: ghcr.io/bluesky-social/pds:0.4 restart: unless-stopped profiles: - bluesky environment: PDS_HOSTNAME: ${PDS_DOMAIN_NAME} PDS_JWT_SECRET: ${PDS_JWT_SECRET} PDS_ADMIN_PASSWORD: ${PDS_ADMIN_PASSWORD} PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ${PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX} PDS_DATA_DIRECTORY: /pds PDS_BLOBSTORE_DISK_LOCATION: /pds/blocks PDS_BLOB_UPLOAD_LIMIT: 157286400 PDS_DID_PLC_URL: "https://plc.directory" PDS_BSKY_APP_VIEW_URL: "https://api.bsky.app" PDS_BSKY_APP_VIEW_DID: "did:web:api.bsky.app" PDS_REPORT_SERVICE_URL: "https://mod.bsky.app" PDS_REPORT_SERVICE_DID: "did:plc:ar7c4by46qjdydhdevvrndac" PDS_CRAWLERS: "https://bsky.network, https://atproto.africa" PDS_EMAIL_SMTP_URL: "smtps://${SMTP_USER}:${SMTP_PASSWORD}@${SMTP_HOST}:${SMTP_PORT}" PDS_EMAIL_FROM_ADDRESS: "${SMTP_FROM}" LOG_ENABLED: true volumes: - pds:/pds pds_worker: <<: *default_backend profiles: - bluesky deploy: mode: replicated replicas: 1 command: "npm exec tsx atproto.ts" cache: <<: *default_backend deploy: mode: replicated replicas: 1 websocket: <<: *default_backend deploy: mode: replicated replicas: 1 command: "npm exec tsx websocket.ts" workers: <<: *default_backend build: <<: *default_backend_build environment: <<: *default_backend_env_vars USE_WORKERS: true deploy: mode: replicated replicas: 3 volumes: dbpg: caddy: pds: frontend: redis: