7.2 The run image installs no group
Read the tutorial. Build it yourself is the other way to read this part.
This walk goes through rahuldave/cheesecracker on GitHub. This part starts from the code at 2a28a5c704d6, partway through step-07. The code before this part
To look at this code on your machine, run these commands. The first one makes a clone, if this folder has none:
[ -d cheesecracker ] || git clone https://github.com/rahuldave/cheesecracker
cd cheesecracker
git fetch --quiet --tags --force origin
git switch --detach 2a28a5c704d6just setupThe whole tutorial then writes the checks of pre-commit, and .dockerignore. The run image is the server as it runs
in production. It needs the dependencies of the project, and no group.
7.2 the run image, the server as it runs in production
The run image needs Python, the dependencies of the server, and the code. It does not need uv, the tests, ruff or
pre-commit. The Dockerfile builds the image in stages, and the run image takes only what the server needs.
The stage uv takes uv from its own image. The stage build installs the dependencies of uv.lock into
/opt/venv, with --no-default-groups, so no test tool and no ruff. The stage run copies /opt/venv and the
package api, and runs uvicorn as the user app, with four workers.
docker build --target run --tag cheesecracker-run .Docker prints each line of the Dockerfile as it runs. Near the end, the line naming to docker.io/library/cheesecracker-run names the image.
docker image ls cheesecracker-runThe image is about 173 MB, nearly all of it Python and the dependencies of the server.
What changed
Dockerfile: the stagesuv,buildandrun. New, 34 lines. The file- look at
RUN uv sync --locked --no-default-groups
- look at
The diff of Dockerfile
@@ -0,0 +1,34 @@
+# The images of the API server, built from uv.lock, so they hold the versions that the lockfile names.
+#
+# docker build --target run --tag cheesecracker-run .
+# the production image: Python, the dependencies of the server and its code. It starts the server
+#
+# The environment lives in /opt/venv, never in the project folder.
+
+# uv itself, copied from its own image
+FROM ghcr.io/astral-sh/uv:0.12.13 AS uv
+
+# The environment of the server, without the dependency groups: no tests, no ruff, no pre-commit
+FROM python:3.12-slim-bookworm AS build
+COPY --from=uv /uv /usr/local/bin/uv
+ENV UV_PROJECT_ENVIRONMENT=/opt/venv \
+ UV_PYTHON_DOWNLOADS=never \
+ UV_COMPILE_BYTECODE=1 \
+ UV_LINK_MODE=copy \
+ UV_NO_CACHE=1
+WORKDIR /app
+COPY pyproject.toml uv.lock .python-version ./
+RUN uv sync --locked --no-default-groups
+
+# The run image: Python, the environment from the build stage, and the code. No uv, and no dev tools
+FROM python:3.12-slim-bookworm AS run
+RUN useradd --create-home --uid 1000 app
+COPY --from=build /opt/venv /opt/venv
+WORKDIR /app
+COPY src/api-service/api ./api
+ENV PATH=/opt/venv/bin:$PATH \
+ PYTHONUNBUFFERED=1
+USER app
+EXPOSE 9000
+# Four workers, and no restart on a change: the server as it runs in production
+CMD ["uvicorn", "api.service:app", "--host", "0.0.0.0", "--port", "9000", "--workers", "4"]This part ends at the code at 0acb4556f724, partway through step-07. The code after this part