Mastering the Modern Python Dockerfile: Optimization and Security
Overview
Most developers begin their containerization journey with a bloated Dockerfile that results in massive images and sluggish deployment pipelines. An unoptimized image often exceeds a gigabyte, dragging down CI/CD performance and increasing cloud storage costs. By moving away from heavy generic OS images like Ubuntu and adopting modern tooling, you can reduce image sizes by over 80% while significantly hardening security.
Prerequisites
To follow this guide, you should have a baseline understanding of Python development and Docker fundamentals. Familiarity with the terminal, basic shell commands, and the concept of virtual environments will help you navigate the more advanced multi-stage build patterns.
Key Libraries & Tools
- FastAPI: A high-performance web framework for building APIs with Python.
- Poetry: A tool for dependency management and packaging in Python.
- uv: An extremely fast Python package installer and resolver written in Rust.
- Debian: The Linux distribution serving as the foundation for most official Python images.

Refined Base Images and Tagging
Standard Python images often include unnecessary build tools and headers. Switching to python:3.13-slim-bookworm provides a minimal Debian base without the bloat. Furthermore, avoid the latest tag; it is a rolling target that introduces unpredictability. Specific tags ensure your production environment remains consistent and reproducible.
The Multi-Stage Build Pattern
This technique separates the build environment from the runtime environment. You use a heavy image with compilers to install dependencies, then copy only the resulting virtual environment into a slim production image.
# Stage 1: Builder
FROM python:3.13-bookworm AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
WORKDIR /app
COPY pyproject.toml .
RUN uv venv && uv sync
# Stage 2: Runtime
FROM python:3.13-slim-bookworm
WORKDIR /app
COPY --from=builder /app/.venv /.venv
ENV PATH="/.venv/bin:$PATH"
COPY ./src ./src
CMD ["uvicorn", "src.main:app"]
Syntax Notes and Performance
Using apt-get install --no-install-recommends prevents the installation of suggested packages that aren't strictly required. Additionally, chaining rm -rf /var/lib/apt/lists/* in the same RUN layer clears out the package index metadata, which serves no purpose in a final production image.
Practical Examples
In a real-world FastAPI deployment, applying these steps took an image from nearly 1GB down to roughly 150MB. By using uv instead of Poetry inside the Dockerfile, build times dropped from 36 seconds to under 9 seconds due to faster dependency resolution.
Tips & Gotchas
Always run your container as a non-root user to prevent attackers from gaining system-level access. Use RUN useradd -m appuser && USER appuser. A common mistake is copying the entire project directory (COPY . .), which can accidentally leak .env files or include heavy local artifacts like node_modules or __pycache__.
- Debian
- 17%· products
- FastAPI
- 17%· products
- Poetry
- 17%· products
- uv
- 17%· products
- Arjan Codes
- 8%· people
- Other topics
- 25%

This Is How You Write an Efficient Python Dockerfile
WatchArjanCodes // 18:47
On this channel, I post videos about programming and software design to help you take your coding skills to the next level. I'm an entrepreneur and a university lecturer in computer science, with more than 20 years of experience in software development and design. If you're a software developer and you want to improve your development skills, and learn more about programming in general, make sure to subscribe for helpful videos. I post a video here every Friday. If you have any suggestion for a topic you'd like me to cover, just leave a comment on any of my videos and I'll take it under consideration. Thanks for watching!