build-python-dockerfiles
Build Python Dockerfiles
Use this skill to author Dockerfiles for Python projects using uv and multi-stage builds.
The default pattern is based on Hynek Schlawack's article Production-ready Python Docker Containers with uv.
Workflow
- Detect whether the project is packaged (installable) or unpackaged (run from copied source).
- Choose a base image strategy:
- Default to
python:<version>-slimfor generic Python services. - Use an OS image like
ubuntu:noblewhen system packages or org-standard base images matter. - Keep build and runtime on the same distro family.
- Avoid Alpine unless the user has a hard requirement.
- Default to
- Install dependencies before copying source so lockfile changes and app code changes stay in separate layers.
- Keep dependency sync and app install in separate
uv syncsteps. - Pick the runtime command pattern that matches the app type.
- Verify the final Dockerfile against the checklist in this file.
Required build patterns
- Use two stages:
buildandfinal. - Add layers in inverse order of likely change frequency.
- Install dependencies before copying source to maximize cache hits.
- Keep dependency sync and app install in separate
RUN uv syncsteps. - Use BuildKit cache mounts for the
uvcache. - Use
UV_PROJECT_ENVIRONMENT=/appand copy/appinto runtime image. - Byte-compile Python files for faster startup with
UV_COMPILE_BYTECODE=1. - Run as non-root in runtime.
- Prefer
uv sync --lockedfor deployment builds.
Default settings
- Copy
uvfromghcr.io/astral-sh/uv:<version>. - Set:
UV_LINK_MODE=copyUV_COMPILE_BYTECODE=1UV_PYTHON_DOWNLOADS=neverUV_PROJECT_ENVIRONMENT=/app
- Set
UV_PYTHONwhen the base image has multiple Python interpreters or whenuvcannot infer the intended one cleanly. - Use BuildKit cache mounts for the
uvcache, typically/root/.cache/uvor/root/.cache. - Never bake secrets into the Dockerfile via
ENV.
Baseline template
Use this as the default starting point for packaged applications:
# syntax=docker/dockerfile:1.9
FROM python:3.13-slim AS build
COPY /uv /uvx /bin/
ENV UV_LINK_MODE=copy \
UV_COMPILE_BYTECODE=1 \
UV_PYTHON_DOWNLOADS=never \
UV_PROJECT_ENVIRONMENT=/app
WORKDIR /src
COPY pyproject.toml uv.lock ./
RUN \
uv sync --locked --no-dev --no-install-project
COPY . /src
RUN \
uv sync --locked --no-dev --no-editable
FROM python:3.13-slim AS final
ENV PATH="/app/bin:$PATH"
STOPSIGNAL SIGINT
RUN groupadd --system app && useradd --system --gid app --home-dir /app app
COPY /app /app
USER app
WORKDIR /app
CMD ["python", "-m", "your_package"]
Keep the following adaptation rules in mind:
- If the project is not packaged, skip the second
uv syncstep and copy the source into the runtime image after copying/app. - If the project has heavy OS-level build dependencies, use a fuller build image and only install runtime libraries in the final image.
- If the project needs a shell entrypoint or signal handling wrapper, use an explicit
ENTRYPOINTscript and keepSTOPSIGNAL SIGINT. - When using an OS image instead of
python:<version>-slim, install the exact runtime Python packages explicitly and setUV_PYTHONif needed.
Entrypoint patterns
Pick the startup command that matches the project type.
ASGI (FastAPI, Starlette, Quart)
EXPOSE 8000
CMD ["uvicorn", "your_package.main:app", "--host", "0.0.0.0", "--port", "8000"]
Django (WSGI)
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "your_project.wsgi:application"]
Flask or generic WSGI
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "your_package.wsgi:app"]
Background worker
CMD ["python", "-m", "your_package.worker"]
CLI or batch task container
ENTRYPOINT ["python", "-m", "your_package.cli"]
Use JSON-array syntax for CMD and ENTRYPOINT.
Final checklist
Run this checklist before finalizing output:
- Multi-stage build is used.
uvis copied from the official image.- Dependency installation happens before source copy.
- Dependency sync and application install are separate when the app is packaged.
- A BuildKit cache mount is used for
uv. UV_PROJECT_ENVIRONMENT=/appis set and/appis copied into the runtime image.- The runtime image runs as a non-root user.
PATHincludes/app/binwhen executables live in the project environment.- Startup command is explicit and matches app type.
- No secrets are baked into the image.
- Optional but recommended:
STOPSIGNAL SIGINT. - Optional but recommended:
EXPOSEfor network services. - Optional but recommended: a
.dockerignorethat excludes VCS data, caches, build artifacts, and local virtualenvs.
Output contract
When generating a Dockerfile for a user:
- Return a complete Dockerfile.
- State assumptions briefly (Python version, packaged vs unpackaged, startup command).
- Add a short
.dockerignoresuggestion when missing.
More from baggiponte/skills
codebase-librarian
Create a comprehensive inventory of a codebase. Map structure, entry points, services, infrastructure, domain models, and data flows. Pure documentation—no opinions or recommendations. Use when onboarding to an unfamiliar codebase, documenting existing architecture before changes, preparing for architecture reviews or migration planning, or creating a reference for the team. Triggers on requests like "map this codebase", "document the architecture", "create an inventory", or "what does this codebase contain".
20code-refactor
Systematic refactoring of codebase components through a structured 3-phase process. Use when asked to refactor, restructure, or improve specific components, modules, or areas of code. Produces research documentation, change proposals with code samples, and test plans. Triggers on requests like "refactor the authentication module", "restructure the data layer", "improve the API handlers", or "clean up the payment service".
12architecture-design-critique
Perform a codebase-wide architectural review through a Ports & Adapters (hexagonal architecture) lens. Assess boundary violations, coupling issues, and dependency direction. Produces a prioritized improvement roadmap. Use when reviewing architecture for testability/portability, assessing technical debt, planning refactoring efforts, or evaluating codebase health. Triggers on requests like "review the architecture", "assess coupling", "hexagonal analysis", "check boundary violations", or "architecture critique".
9oracle
Submit an implementation plan to GPT 5.2-xhigh for review by a senior engineer persona. The Oracle provides direct, honest critique focused on simplicity and pragmatic excellence. Use when you have a detailed implementation plan and want a second opinion before coding. Triggers on "review my plan", "get Oracle feedback", "consult the Oracle", "plan review", or when you want senior engineer critique on an implementation approach.
2context7
Retrieve up-to-date documentation for software libraries, frameworks, and components via the Context7 CLI using `bunx ctx7`. Use this whenever you need current docs, API references, code examples, migration details, or verification for a library or framework instead of relying on training data. Triggers on requests like "look up the docs for X", "find the latest API for Y", "show me examples from the docs", "check the current React/Next.js/FastAPI docs", or "verify this library usage against current documentation".
2spark-context-curator
Ultra-fast, read-only codebase exploration and context curation for GPT-5.3 Codex Spark. Use when you need deep repository understanding without modifying anything: architecture mapping, flow tracing, ownership discovery, incident/code-review prep, or implementation planning. Triggers on requests like \"explore this codebase\", \"curate context\", \"map where X happens\", \"investigate before editing\", or \"read-only deep dive\".
2