From 12b699f7e21ae51ce0fbbf421959749cbdb95a07 Mon Sep 17 00:00:00 2001 From: Brandon Ros Date: Tue, 21 Apr 2026 22:43:03 -0400 Subject: [PATCH 1/3] Harden Dockerfile for production: pin, freeze, drop runtime uv The previous Dockerfile was a dev setup masquerading as production: - `FROM python:latest` meant rebuilds drifted silently over time. - `uv sync --locked` + `uv run` at CMD meant every container start re-validated the lockfile against the venv, which can touch the package index (at minimum DNS) even when the venv is already built. - `--reload` is uvicorn's dev mode; it watches the filesystem and respawns workers, each of which re-invokes the CMD and compounds the above. This rewrite: - Pins the base image (python:3.12-slim) and uv (0.4.30) so builds are reproducible. - Uses a multi-stage build so the runtime image ships neither uv nor pip caches. - Switches `--locked` -> `--frozen` so lockfile drift fails the build loudly instead of being silently reconciled. - Splits the dep-install layer from the source-copy layer so code changes don't bust the dependency cache. - Drops `uv run` from CMD and invokes uvicorn directly from /app/.venv/bin via PATH. Runtime startup now makes zero PyPI callouts and needs no DNS for package management. - Removes `--reload`. Co-Authored-By: Claude Opus 4.7 (1M context) --- Dockerfile | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index f701558a..713ccdf1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,20 @@ -FROM python:latest +FROM python:3.12-slim AS build -WORKDIR /claude-code-proxy +WORKDIR /app +RUN pip install --no-cache-dir uv==0.4.30 -# Copy package specifications COPY pyproject.toml uv.lock ./ +RUN uv sync --frozen --no-install-project --no-dev -# Install uv and project dependencies -RUN pip install --upgrade uv && uv sync --locked - -# Copy project code to current directory COPY . . +RUN uv sync --frozen --no-dev + + +FROM python:3.12-slim + +WORKDIR /app +COPY --from=build /app /app +ENV PATH=/app/.venv/bin:$PATH -# Start the proxy EXPOSE 8082 -CMD uv run uvicorn server:app --host 0.0.0.0 --port 8082 --reload +CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8082"] From 862193da01f8cbd8295a85f7cdec1160371580a5 Mon Sep 17 00:00:00 2001 From: Brandon Ros Date: Tue, 21 Apr 2026 22:51:06 -0400 Subject: [PATCH 2/3] Force uv to use system Python in Dockerfile build Without these env vars, `uv sync` downloads its own CPython into /root/.local/share/uv/python/ and symlinks the venv's interpreter to it. In a multi-stage build we only COPY /app across, so that interpreter directory is absent from the runtime image and the venv's python symlink dangles -- exec fails at container start with "No such file or directory" on /app/.venv/bin/uvicorn. Silent version drift too: uv picked 3.10 despite the 3.12-slim base. UV_PYTHON_PREFERENCE=only-system + UV_PYTHON_DOWNLOADS=never force uv to use /usr/local/bin/python3.12 from the base image, which is present in both stages. Co-Authored-By: Claude Opus 4.7 (1M context) --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 713ccdf1..d2da72c0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,9 @@ FROM python:3.12-slim AS build WORKDIR /app RUN pip install --no-cache-dir uv==0.4.30 +ENV UV_PYTHON_PREFERENCE=only-system \ + UV_PYTHON_DOWNLOADS=never + COPY pyproject.toml uv.lock ./ RUN uv sync --frozen --no-install-project --no-dev From 710ea2cc6e2527c0956fc02d4deca44fa5af6375 Mon Sep 17 00:00:00 2001 From: Brandon Ros Date: Tue, 21 Apr 2026 22:53:22 -0400 Subject: [PATCH 3/3] Align Dockerfile Python to host .python-version (3.10) The host pins Python 3.10 via .python-version for local dev. Keeping the container on the same version avoids subtle behavior differences between developer machines and production (stdlib changes, dep wheel selection, etc.) and means `uv sync` respects the existing pin inside the build context without extra workarounds. Co-Authored-By: Claude Opus 4.7 (1M context) --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index d2da72c0..5140b806 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12-slim AS build +FROM python:3.10-slim AS build WORKDIR /app RUN pip install --no-cache-dir uv==0.4.30 @@ -13,7 +13,7 @@ COPY . . RUN uv sync --frozen --no-dev -FROM python:3.12-slim +FROM python:3.10-slim WORKDIR /app COPY --from=build /app /app