# live.f12.rocks — event photo pipeline: SFTP inbox -> gmic/rembg -> web. # # All three services share one host directory (default ./data, see # .env.example DATA_HOST_DIR) at different mount points: # sftpgo -> /srv/sftpgo/data (SFTP user home = /srv/sftpgo/data/inbox) # worker -> /data (watches /data/inbox, writes /data/jobs) # web -> /data (reads jobs, writes remix variants) # # Copy .env.example to .env and fill in real SFTP credentials before # running this. Never commit .env. services: sftpgo: image: drakkan/sftpgo:v2 restart: unless-stopped # The image defaults to a fixed uid 1000 user. worker/web run as root # (see Dockerfile), and Docker itself creates first-run bind-mount # directories (./data/inbox, ./data/jobs, ...) as root:root — running # sftpgo as root too avoids a uid mismatch on the shared ./data tree. user: root entrypoint: ["/bin/sh", "/entrypoint.sh"] environment: SFTP_USER: ${SFTP_USER:-livef12} SFTP_PASSWORD: ${SFTP_PASSWORD:-changeme} # Minutes. Default SFTPGo is 15 — too aggressive for Android clients # that idle after a successful upload (shows up as EOF in the logs). SFTPGO_COMMON__IDLE_TIMEOUT: ${SFTPGO_IDLE_TIMEOUT:-120} volumes: - ${DATA_HOST_DIR:-./data}:/srv/sftpgo/data - sftpgo_state:/var/lib/sftpgo - ./sftpgo/entrypoint.sh:/entrypoint.sh:ro ports: # SFTP only. The SFTPGo web admin (container port 8080) is # intentionally NOT published here and NOT attached to the # `traefik` network — it stays unreachable from outside docker. # # Host SFTP port. Spec asked for 121212 which exceeds TCP max # (65535); production default is 12121. - "${SFTP_HOST_PORT:-12121}:2022" networks: - internal worker: build: . restart: unless-stopped command: ["python3", "-m", "app.worker"] depends_on: - sftpgo environment: DATA_DIR: /data OUTPUT_COUNT: ${OUTPUT_COUNT:-3} BLEND_OPACITY: ${BLEND_OPACITY:-30%} BLEND_OPACITY_MIN: ${BLEND_OPACITY_MIN:-10} BLEND_OPACITY_MAX: ${BLEND_OPACITY_MAX:-50} FILTER_TIMEOUT: ${FILTER_TIMEOUT:-120} FILTER_TIMEOUT_LONG: ${FILTER_TIMEOUT_LONG:-300} MAX_FILTER_ATTEMPTS: ${MAX_FILTER_ATTEMPTS:-8} MAX_EDGE_PX: ${MAX_EDGE_PX:-2000} REMBG_MODEL: ${REMBG_MODEL:-u2net} REMBG_ALPHA: ${REMBG_ALPHA:-1} NICE_LEVEL: ${NICE_LEVEL:-18} volumes: - ${DATA_HOST_DIR:-./data}:/data - ./assets:/data/assets:ro - rembg_cache:/app/.home # Sequential-only by design (no threads/async in worker.py); these # limits just make sure gmic/rembg can't starve the host too. # Keep headroom on the ~8GB host — birefnet-general needs more than we have. cpus: "1.0" mem_limit: 4g networks: - internal web: build: . restart: unless-stopped command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] depends_on: - worker environment: DATA_DIR: /data OUTPUT_COUNT: ${OUTPUT_COUNT:-3} BLEND_OPACITY: ${BLEND_OPACITY:-30%} BLEND_OPACITY_MIN: ${BLEND_OPACITY_MIN:-10} BLEND_OPACITY_MAX: ${BLEND_OPACITY_MAX:-50} FILTER_TIMEOUT: ${FILTER_TIMEOUT:-120} FILTER_TIMEOUT_LONG: ${FILTER_TIMEOUT_LONG:-300} MAX_FILTER_ATTEMPTS: ${MAX_FILTER_ATTEMPTS:-8} MAX_EDGE_PX: ${MAX_EDGE_PX:-2000} REMBG_MODEL: ${REMBG_MODEL:-u2net} REMBG_ALPHA: ${REMBG_ALPHA:-1} NICE_LEVEL: ${NICE_LEVEL:-18} SITE_TITLE: live.f12.rocks volumes: - ${DATA_HOST_DIR:-./data}:/data - ./assets:/data/assets:ro labels: - "traefik.enable=true" - "traefik.http.routers.livef12rocks.rule=Host(`live.f12.rocks`)" - "traefik.http.routers.livef12rocks.entrypoints=websecure" - "traefik.http.routers.livef12rocks.tls.certresolver=myresolver" - "traefik.http.services.livef12rocks.loadbalancer.server.port=8000" networks: - internal - traefik networks: internal: {} traefik: external: true volumes: sftpgo_state: rembg_cache: