# Production overlay. On the server: # cp deploy/production.env.example deploy/production.env # fill in secrets # docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file deploy/production.env up -d --build # # Adds Caddy (HTTPS on 80/443) in front, keeps the database, Redis, API and web # app off the public internet, and runs nightly database backups. x-prod-api: &prod-api env_file: deploy/production.env environment: NODE_ENV: production DATABASE_URL: postgres://musicshare:${POSTGRES_PASSWORD:?set in production.env}@postgres:5432/musicshare REDIS_URL: redis://redis:6379 WEB_URL: https://${DOMAIN} API_PUBLIC_URL: https://${DOMAIN}/api COOKIE_SECURE: 'true' # Hosted: never let users point us at private networks. ALLOW_PRIVATE_SERVER_URLS: 'false' services: postgres: environment: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set in production.env} ports: !reset [] redis: ports: !reset [] migrate: <<: *prod-api api: <<: *prod-api ports: !reset [] worker: <<: *prod-api web: build: args: NEXT_PUBLIC_API_URL: https://${DOMAIN}/api NEXT_PUBLIC_DONATION_URL: ${DONATION_URL:-} ports: !reset [] caddy: image: caddy:2 restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" # HTTP/3 environment: DOMAIN: ${DOMAIN:?set in production.env} volumes: - ./deploy/Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: - web - api # Nightly pg_dump into ./backups, keeping the last 14 days. backup: image: postgres:17 restart: unless-stopped environment: PGPASSWORD: ${POSTGRES_PASSWORD:?set in production.env} volumes: - ./backups:/backups entrypoint: ["/bin/sh", "-c"] command: - | while true; do f=/backups/musicshare-$$(date +%Y%m%d-%H%M).sql.gz pg_dump -h postgres -U musicshare musicshare | gzip > "$$f" && echo "backup ok: $$f" || echo "backup FAILED" find /backups -name 'musicshare-*.sql.gz' -mtime +14 -delete sleep 86400 done depends_on: postgres: condition: service_healthy volumes: caddy_data: caddy_config: