services: postgres: image: postgres:16-alpine restart: unless-stopped env_file: .env command: ["postgres", "-c", "timezone=${TZ:-UTC}", "-c", "log_timezone=${TZ:-UTC}"] volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 5s timeout: 3s retries: 10 app: # Prebuilt multi-arch image; when it can't be pulled (or with --build) Compose # builds it from this checkout instead. image: ghcr.io/discodrive-cloud/discodrive:${DISCODRIVE_VERSION:-latest} build: . # HTTP backend is reachable only inside the Compose network. environment: APP_HOST: "0.0.0.0" ALLOW_INSECURE_HTTP: "false" # Only nginx and postgres share this private network; never publish app ports. TRUSTED_PROXY_CIDRS: "${TRUSTED_PROXY_CIDRS:-172.16.0.0/12,192.168.0.0/16,10.0.0.0/8}" restart: unless-stopped env_file: .env volumes: - ./data:/data expose: - "${APP_PORT:-8080}" depends_on: postgres: condition: service_healthy init: condition: service_completed_successfully # One-shot, as root: writes a self-signed pair into deploy/nginx/certs unless one is # already there, and hands a fresh ./data to the server account. init: image: ghcr.io/discodrive-cloud/discodrive:${DISCODRIVE_VERSION:-latest} build: . user: "0:0" command: ["init", "-certs", "/certs", "-host", "${BASE_DOMAIN:-localhost},localhost", "-data", "/data"] volumes: - ./deploy/nginx/certs:/certs - ./data:/data nginx: image: nginx:alpine restart: unless-stopped # The image substitutes only explicit deployment variables in the nginx template. # Preserve nginx runtime variables ($host, $scheme, $remote_addr, ...). environment: APP_PORT: "${APP_PORT:-8080}" NGINX_CLOUDFLARED_PEER: "${NGINX_CLOUDFLARED_PEER:-unix:}" NGINX_ENVSUBST_FILTER: "^(APP_PORT|NGINX_CLOUDFLARED_PEER)$$" TZ: "${TZ:-UTC}" volumes: - ./deploy/nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro # data is also mounted into nginx (read-only) for zero-copy X-Accel serving - ./data:/data:ro # TLS is required for every client; the init service fills this with a self-signed # pair on first start — replace it with trusted certificates for production - ./deploy/nginx/certs:/etc/nginx/certs:ro ports: - "${NGINX_HTTPS_PORT:-8443}:443" depends_on: app: condition: service_started init: condition: service_completed_successfully # Face service for «People» in the gallery; off unless COMPOSE_PROFILES=faces, and the # server uses it only with PHOTOS_ML_URL=http://ml:3003 (both in .env). ~600 MB image. ml: image: ghcr.io/discodrive-cloud/discodrive-ml:${DISCODRIVE_VERSION:-latest} build: ./ml profiles: ["faces"] restart: unless-stopped environment: ML_THREADS: "${ML_THREADS:-2}" ML_CONCURRENCY: "${ML_CONCURRENCY:-2}" expose: - "3003" volumes: pgdata: