# Usage: # make build # Build all Docker images (admin + worker) # docker compose up -d # Start all services # # Both admin and worker use python:3.11-slim — the builder stage # is shared (caches deps), then: # - admin: no Chrome needed (FastAPI only) # - worker: installs Chromium + patches ChromeDriver for stealth # ── Build args ───────────────────────────────────────────────────────────────── ARG BASE_IMAGE=python:3.11-slim # ── Stage 1: dependency resolver ───────────────────────────────────────────── FROM python:3.11-slim AS builder # Keep the build independent of GHCR; dependencies already use official PyPI. RUN pip install --no-cache-dir uv==0.11.31 WORKDIR /app # Copy only dependency manifests first (cache layer) COPY pyproject.toml uv.lock* ./ # Install deps into an isolated prefix so we can copy them cleanly RUN uv sync --frozen --no-dev --no-install-project # ── Stage 2a: admin runtime (python:3.11-slim, no Chrome) ──────────────────── FROM ${BASE_IMAGE} AS admin WORKDIR /app ENV PATH="/app/.venv/bin:$PATH" \ PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 COPY . . COPY --from=builder /app/.venv /app/.venv RUN mkdir -p data logs recordings \ && useradd -m -u 1001 seed \ && chown -R seed:seed /app USER seed EXPOSE 8000 CMD ["python", "main.py", "admin"] # ── Stage 2b: worker runtime (Python image with Chromium installed) ─────────── # Playwright Docker images don't ship Python — they're language-agnostic # Ubuntu images with only Chromium/Chrome/Firefox. Reverting to python:3.11-slim # for the worker so we get a working Python + pip out of the box. FROM ${BASE_IMAGE} AS worker ENV PATH="/app/.venv/bin:$PATH" \ PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ # Chromium binary (installed below) CHROME_BINARY=/usr/bin/chromium \ CHROMEDRIVER_PATH=/app/drivers/chromedriver \ # Always run headless inside Docker HEADLESS=true WORKDIR /app # Install Chromium + matching system ChromeDriver RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates curl chromium chromium-driver ffmpeg xvfb \ && rm -rf /var/lib/apt/lists/* # Copy manifests + lockfile first (stable cache layer) COPY pyproject.toml uv.lock* ./ # Install deps into a venv RUN pip install --no-cache-dir uv==0.11.31 \ && uv sync --frozen --no-dev --no-install-project # Copy app code (busts cache on code changes, not deps) COPY . . # Patch ChromeDriver at build time. This downloads the matching version, # patches out undetected-chromedriver anti-detection flags, and places # the binary at /app/drivers/chromedriver (configured via CHROMEDRIVER_PATH). RUN PYTHONPATH=. python scripts/patch_driver.py \ && mkdir -p data logs recordings # Non-root user for safety RUN useradd -m -u 1001 seed 2>/dev/null || true \ && chown -R seed:seed /app 2>/dev/null || true USER seed # CMD is overridden in docker-compose.yml to use RQ worker CMD ["rq", "worker", "--url", "redis://redis:6379/0", "batch"]