# syntax=docker/dockerfile:1.7 FROM python:3.14-slim AS builder ENV PDM_CHECK_UPDATE=false \ PDM_VENV_IN_PROJECT=true \ PDM_NO_EDITABLE=true \ PIP_NO_CACHE_DIR=1 \ PYTHONDONTWRITEBYTECODE=1 # Build tools needed by any source-built wheels; curl-cffi ships manylinux # wheels with bundled libcurl-impersonate, so usually no compilation is needed. RUN apt-get update \ && apt-get install -y --no-install-recommends build-essential \ && rm -rf /var/lib/apt/lists/* RUN pip install --no-cache-dir pdm WORKDIR /app # Copy only dependency manifests first so this layer is cached across code changes. COPY pyproject.toml pdm.lock ./ # pyproject references README.md; create a placeholder so metadata resolution works. RUN touch README.md # Install only production (non-dev) dependencies, pinned by the lockfile. RUN pdm install --prod FROM python:3.14-slim AS runtime ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ PATH=/app/.venv/bin:$PATH # ca-certificates for TLS verification; curl-cffi bundles its own libcurl but # still relies on the system CA bundle for peer verification. RUN apt-get update \ && apt-get install -y --no-install-recommends ca-certificates \ && rm -rf /var/lib/apt/lists/* \ && useradd --create-home --uid 10001 app WORKDIR /app # Copy the prebuilt venv and application source as the non-root user. COPY --from=builder --chown=app:app /app/.venv /app/.venv COPY --chown=app:app app.py ./ COPY --chown=app:app src/ ./src/ USER app EXPOSE 8000 # uvicorn behind a reverse proxy: enable proxy-header trust if you front it. HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ CMD python -c "import urllib.request as u; u.urlopen('http://127.0.0.1:8000/health').read()" CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers", "--forwarded-allow-ips=*"]