Shared Docker Images
DCC-BS keeps its Docker logic in one place so apps don't duplicate it. The dcc-docker-images repository ships a pre-built mise base image plus two Dockerfile templates that every app copies in and builds with its own source as the build context.
Layout
dcc-docker-images/
├── mise/ # PRE-BUILT base image (dcc-mise) — build once, share
├── fastapi/ # TEMPLATE — copy into FastAPI/uv apps, build in the app repo
├── nuxt/ # TEMPLATE — copy into Nuxt/node apps, build in the app repo
└── README.mdHow it works
Only the mise base image is pre-built and shared. The fastapi/ and nuxt/ Dockerfiles are templates: they COPY app files, so they must be built inside each app repo with the app as the build context.
The base image carries everything common:
- Debian slim, plus apt packages (
sudo,curl,git,ca-certificates,build-essential) - mise installed to
/mise/bin/mise, with data/config/cache/state dirs under/miseand shims onPATH /usr/local/bin/assemble-runtime— a shared script that strips the mise-managed python/node + varlock into a minimal/runtime, dropping headers, Tcl/Tk, terminfo, npm/corepack, man pages, and pip/idle launchers.
Each app's Dockerfile is then thin: it only holds the app-specific steps (COPY . ., build commands, entrypoint). The runtime-assembly logic lives in exactly one place — the base image — instead of being copy-pasted into every app.
The base image
ghcr.io/dcc-bs/dcc-docker-images/mise is published to GHCR by the .github/workflows/docker-publish.yml workflow (manual workflow_dispatch), tagged with the Debian release and latest.
The tag matches the Debian release. When you bump it, rebuild and re-tag so apps pick up the new assemble-runtime script and toolchain.
Using a template in an app
- Copy the template into the app repo:sh
cp dcc-docker-images/fastapi/Dockerfile /path/to/app/Dockerfile - Build in the app repo (the app is the build context):sh
cd /path/to/app docker build -t my-app .
The python/node version is not hardcoded anywhere in the templates — it comes from the app's own pyproject.toml (requires-python) or mise.toml. Change it there and both build and runtime follow automatically.
The assemble-runtime script
Run inside the build stage after mise install has populated /mise/installs:
assemble-runtime python # FastAPI/uv apps (standalone varlock binary)
assemble-runtime node # Nuxt/node apps (npm varlock package)It writes a minimal runtime to /runtime and, for Python, repoints the app's venv at the relocated interpreter so the python version lives in exactly one place (pyproject.toml / mise.toml), never hardcoded in the script.
Version pinning
The base image tag matches the Debian release. When you bump the base image, rebuild it and re-tag so apps pick up the new assemble-runtime script and toolchain.
Publish
The .github/workflows/docker-publish.yml workflow builds and pushes the mise base image to ghcr.io/dcc-bs/dcc-docker-images/mise on manual dispatch. The fastapi/nuxt templates are not published — they are copied into each app and built there.
See also
- Docker Standards — the general containerization standards
- Tooling with mise — the mise toolchain the base image ships
- Varlock Setup — the secrets workflow the runtime uses
