The image is still a draft, so this is the standard it is being held to, not a finished result.
Pin the base exactly
The base image is a slim Python image, pinned by digest, not just by tag. A tag can be moved to point at something else; a digest can't. The same build input always gives the same base, and an update is a deliberate change in a pull request, not a surprise.
Two stages, and no compiler anywhere
The build happens in two stages. The first creates a virtual environment and installs the pinned dependencies. The second copies that environment into a clean runtime image.
Every dependency installs from a prebuilt wheel, and the build is told to refuse anything else. So there is no compiler or C headers in either stage, and if a dependency ever stops shipping a wheel, the build fails instead of quietly compiling it.
Only what runs
The runtime image gets only the native libraries the code needs at run time, the ones PDF rendering depends on, plus a base font. No package manager caches, no build tools, no extras. Less in the image means less to patch and less to exploit.
Never root
The service runs as its own unprivileged user with a fixed id, so the files it owns on a mounted volume line up the same way every time. State lives on that volume, not in the image, so the image can be thrown away and rebuilt at any point.
Build for the cache
Dependencies are installed before the code is copied in. Code changes every day and dependencies rarely do, so most builds reuse the dependency layer and finish quickly. The package cache lives in a build-time cache mount, so it never ends up in the image.
Checked on every change
Every pull request runs a dependency audit, and a dependency with a known vulnerability blocks the merge. Image scanning in the pipeline comes with the move itself.
None of this is new. It's the list I'd want someone else to have followed before I trusted their image in production.
