services: # ── Admin panel (FastAPI) ──────────────────────────────────────────────────── admin: build: . image: seed:latest container_name: seed-admin restart: unless-stopped command: python main.py admin ports: - "${ADMIN_PORT:-8000}:8000" env_file: - .env environment: # Override host so FastAPI binds on all interfaces inside the container ADMIN_HOST: "0.0.0.0" DB_PATH: /app/data/tracker.db HEADLESS: "true" volumes: - seed-data:/app/data healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/"] interval: 30s timeout: 5s retries: 3 start_period: 10s # ── Crawler (one-shot, started manually or via make docker-otp / docker-fresh) crawler: build: . image: seed:latest container_name: seed-crawler # No restart — run on demand restart: "no" profiles: - crawler # docker compose --profile crawler run crawler python main.py otp env_file: - .env environment: DB_PATH: /app/data/tracker.db HEADLESS: "true" CHROME_BINARY: /usr/bin/google-chrome-stable volumes: - seed-data:/app/data # Chrome needs /dev/shm enlarged to avoid OOM crashes shm_size: "256mb" # Required capabilities for Chrome sandbox inside Docker cap_add: - SYS_ADMIN security_opt: - seccomp:unconfined volumes: # Named volume — SQLite file persists across container restarts and rebuilds seed-data: driver: local