version: "3.8" services: # ── Backend (Rust) ────────────────────────────────────────── server: container_name: paperphone-plus-server image: facilisvelox/paperphone-plus-server:latest ports: - "3000:3000" environment: PORT: 3000 JWT_SECRET: ${JWT_SECRET:-change_me_to_a_random_string} # Access JWTs last 30 minutes; 90-day device refresh sessions are stored in MySQL. DB_HOST: mysql DB_PORT: 3306 DB_USER: ${DB_USER:-paperphoneplus} DB_PASS: ${DB_PASS:-paperphoneplus} DB_NAME: ${DB_NAME:-paperphoneplus} REDIS_HOST: redis REDIS_PORT: 6379 REDIS_PASS: ${REDIS_PASS:?Set REDIS_PASS in your environment or .env file} UPLOAD_DIR: ./uploads CHAT_FILE_RETENTION_DAYS: ${CHAT_FILE_RETENTION_DAYS:-14} FCM_PROJECT_ID: ${FCM_PROJECT_ID:-} FCM_CLIENT_EMAIL: ${FCM_CLIENT_EMAIL:-} FCM_PRIVATE_KEY: ${FCM_PRIVATE_KEY:-} FCM_RELAY_SECRET: ${FCM_RELAY_SECRET:-} FCM_RELAY_URL: ${FCM_RELAY_URL:-} FCM_RELAY_KEY: ${FCM_RELAY_KEY:-} # Required for all direct calls and group meetings. Must be reachable by clients. LIVEKIT_URL: ${LIVEKIT_URL:-ws://localhost:7880} LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:-paperphone} LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:-paperphone_change_this_secret} BARK_ALLOWED_HOSTS: ${BARK_ALLOWED_HOSTS:-api.day.app} NTFY_BASE_URL: ${NTFY_BASE_URL:-https://ntfy.sh} NTFY_TOKEN: ${NTFY_TOKEN:-} APNS_TEAM_ID: ${APNS_TEAM_ID:-} APNS_KEY_ID: ${APNS_KEY_ID:-} APNS_PRIVATE_KEY: ${APNS_PRIVATE_KEY:-} APNS_BUNDLE_ID: ${APNS_BUNDLE_ID:-} APNS_SANDBOX: ${APNS_SANDBOX:-false} APNS_RELAY_SECRET: ${APNS_RELAY_SECRET:-} APNS_RELAY_URL: ${APNS_RELAY_URL:-} APNS_RELAY_KEY: ${APNS_RELAY_KEY:-} TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:-} STICKER_PACKS: ${STICKER_PACKS:-} ADMIN_PATH: ${ADMIN_PATH:-/admin} ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123} volumes: - uploads:/app/uploads depends_on: mysql: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD", "curl", "-sf", "http://localhost:3000/health"] interval: 15s timeout: 5s retries: 5 # Existing databases may need reliability-schema migrations before /health is available. start_period: 120s restart: unless-stopped # SFU media server for all direct calls and group meetings. livekit: container_name: paperphone-livekit image: livekit/livekit-server:latest environment: LIVEKIT_CONFIG: | port: 7880 rtc: tcp_port: 7881 udp_port: 7882 use_external_ip: true redis: address: redis:6379 password: ${REDIS_PASS:?Set REDIS_PASS in your environment or .env file} keys: ${LIVEKIT_API_KEY:-paperphone}: ${LIVEKIT_API_SECRET:-paperphone_change_this_secret} ports: - "7880:7880" - "7881:7881" - "7882:7882/udp" depends_on: redis: condition: service_healthy restart: unless-stopped # ── MySQL 8.0 ─────────────────────────────────────────────── mysql: container_name: paperphone-mysql image: mysql:8.0 command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} MYSQL_DATABASE: ${DB_NAME:-paperphoneplus} MYSQL_USER: ${DB_USER:-paperphoneplus} MYSQL_PASSWORD: ${DB_PASS:-paperphoneplus} volumes: - mysql_data:/var/lib/mysql - ./server/src/db/schema.sql:/docker-entrypoint-initdb.d/01-schema.sql:ro ports: - "3306:3306" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 10 start_period: 30s restart: unless-stopped # ── Redis 7 ───────────────────────────────────────────────── redis: container_name: paperphone-redis image: redis:7-alpine command: ["redis-server", "--requirepass", "${REDIS_PASS:?Set REDIS_PASS in your environment or .env file}"] environment: REDIS_PASS: ${REDIS_PASS:?Set REDIS_PASS in your environment or .env file} volumes: - redis_data:/data ports: - "6379:6379" healthcheck: test: ["CMD-SHELL", "redis-cli --no-auth-warning -a \"$$REDIS_PASS\" ping | grep -q PONG"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped volumes: mysql_data: redis_data: uploads: name: paperphone-plus-uploads