# US08-02 — Build a Reproducible Application Image Epic: [E08](../E08-container-deployment.md) As an operator, I want one image that can run either application role, so deployment is a pull instead of a Python environment I have to reproduce by hand. ## Context The application shells out to `exiftool` and `immich-go`, writes into the library as a normal filesystem user, and serves a static frontend from `frontend/`. All three have to be true inside the image, or the container starts and then fails on the first real operation. ## Acceptance criteria - A `Dockerfile` builds from a pinned Python base, installs the project and its runtime dependencies, and contains no test, playwright, or build-only tooling in the final layer. - `exiftool` and `immich-go` are present at pinned versions, and their versions are recorded in the image and reported by `python -m photo_pipeline diagnostics`. - The image runs as a non-root user whose UID/GID are build-time arguments, so files the application renames or writes keep the ownership the host library expects. - One entrypoint selects the role: `serve` or `worker`, passing through the existing CLI arguments; no supervisor runs two roles in one container. - `serve` containers declare a `HEALTHCHECK` against `/api/v1/health/ready`, so an unmigrated or misconfigured database is not reported healthy. - The image contains no secrets, no library data, no database, and no `.git`; the build context is constrained by `.dockerignore`. - Image build is reproducible from a clean checkout and documented in `README.md`. ## Automated tests - A build-and-run test asserts the image starts, reports ready, serves the frontend index, and returns the pinned `exiftool` and `immich-go` versions. - A test asserts the container refuses to run as UID 0 and that a file created by the container is owned by the configured UID/GID. ## Dependencies - US07-05