# ── Stage 1: dependency resolver ───────────────────────────────────────────── FROM python:3.11-slim AS builder # Install uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv 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 2: runtime ────────────────────────────────────────────────────────── FROM python:3.11-slim AS runtime # ── Chrome + system deps ────────────────────────────────────────────────────── RUN apt-get update && apt-get install -y --no-install-recommends \ wget gnupg ca-certificates curl unzip \ # X11 / rendering libs needed even in headless mode libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \ libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \ libgbm1 libasound2 libpango-1.0-0 libcairo2 libx11-xcb1 \ && wget -qO- https://dl.google.com/linux/linux_signing_key.pub \ | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg \ && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] \ http://dl.google.com/linux/chrome/deb/ stable main" \ > /etc/apt/sources.list.d/google-chrome.list \ && apt-get update \ && apt-get install -y --no-install-recommends google-chrome-stable \ && rm -rf /var/lib/apt/lists/* # ── uv + venv from builder ──────────────────────────────────────────────────── COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv COPY --from=builder /app/.venv /app/.venv WORKDIR /app # Make the venv's binaries the default Python ENV PATH="/app/.venv/bin:$PATH" \ PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ # Tell undetected-chromedriver where Chrome lives CHROME_BINARY=/usr/bin/google-chrome-stable \ # Always run headless inside Docker HEADLESS=true # ── Application code ────────────────────────────────────────────────────────── COPY . . RUN mkdir -p data # Non-root user for safety RUN useradd -m -u 1001 seed && chown -R seed:seed /app USER seed EXPOSE 8000 CMD ["python", "main.py", "admin"]