Optimal Dockerfile for Python with poetry
Looking for faster Python builds? We recommend using UV instead of Poetry for
significantly faster dependency installation and better caching. UV supports Poetry projects natively and can speed up
your builds by 10-100x while maintaining full compatibility with your pyproject.toml and poetry.lock files.
Below is an example Dockerfile that we recommend at Depot for building Docker images for Python applications that use poetry as their package manager.
# syntax=docker/dockerfile:1
FROM python:3.13-slim AS build
ENV POETRY_VERSION=2.2.1 \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
POETRY_HOME="/opt/poetry" \
POETRY_VIRTUALENVS_IN_PROJECT=true \
POETRY_NO_INTERACTION=1 \
PYSETUP_PATH="/opt/pysetup" \
VENV_PATH="/opt/pysetup/.venv"
ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"
RUN pip install "poetry==$POETRY_VERSION"
WORKDIR $PYSETUP_PATH
COPY poetry.lock pyproject.toml ./
RUN --mount=type=cache,target=/root/.cache/pypoetry \
poetry install --no-root
COPY . .
FROM python:3.13-slim AS runtime
ENV VENV_PATH="/opt/pysetup/.venv" \
PATH="/opt/pysetup/.venv/bin:$PATH"
RUN groupadd -g 1001 appgroup && \
useradd -u 1001 -g appgroup -m -d /home/appuser -s /bin/bash appuser
WORKDIR /app
COPY --from=build --chown=appuser:appgroup /opt/pysetup/.venv /opt/pysetup/.venv
COPY --from=build --chown=appuser:appgroup /opt/pysetup/ ./
USER appuser
ENTRYPOINT ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]Explanation of the Dockerfile
This Dockerfile uses an optimized approach for Python applications using Poetry, featuring multi-stage builds and security optimizations.
At a high level, here are the things we're optimizing in our Docker build for a Python application with Poetry:
- Multi-stage builds for smaller final images
- Poetry cache mounts for dependency caching
- Security optimizations with non-root users
Stage 1: FROM python:3.13-slim AS build
FROM python:3.13-slim AS build
ENV POETRY_VERSION=2.2.1 \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
POETRY_HOME="/opt/poetry" \
POETRY_VIRTUALENVS_IN_PROJECT=true \
POETRY_NO_INTERACTION=1 \
PYSETUP_PATH="/opt/pysetup" \
VENV_PATH="/opt/pysetup/.venv"
ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"We start with Python 3.13 slim for a smaller base image and configure Poetry with specific environment variables:
POETRY_VERSION=2.2.1pins the Poetry version for reproducible buildsPOETRY_VIRTUALENVS_IN_PROJECT=truecreates virtual environments inside the projectPOETRY_NO_INTERACTION=1disables interactive promptsPYTHONUNBUFFERED=1ensures logs are output in real-time
Installing Poetry
RUN --mount=type=cache,target=/root/.cache \
pip install "poetry==$POETRY_VERSION"We install Poetry using pip with cache mounts for efficiency.
Dependency installation
WORKDIR $PYSETUP_PATH
COPY poetry.lock pyproject.toml ./
RUN --mount=type=cache,target=/root/.cache/pypoetry \
poetry install --no-rootWe copy the Poetry configuration files and install dependencies without installing the project itself first.
Source code installation
COPY . .After dependencies are installed, we copy the source code.
Stage 2: FROM python:3.13-slim AS runtime
FROM python:3.13-slim AS runtime
ENV VENV_PATH="/opt/pysetup/.venv" \
PATH="/opt/pysetup/.venv/bin:$PATH"
RUN groupadd -g 1001 appgroup && \
useradd -u 1001 -g appgroup -m -d /home/appuser -s /bin/bash appuser
WORKDIR /app
COPY --from=build --chown=appuser:appgroup /opt/pysetup/.venv /opt/pysetup/.venv
COPY --from=build --chown=appuser:appgroup /opt/pysetup/ ./
USER appuser
ENTRYPOINT ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]The runtime stage starts with a clean slim image and creates a non-root user for security. We copy the virtual environment and project files from the build stage and set proper ownership.
Understanding BuildKit Cache Mounts
Cache mounts in this Dockerfile speed up builds by persisting the package manager cache. This means that even when a layer needs to be rebuilt, your package manager only fetches what's new or updated. This Dockerfile uses multiple cache mounts:
RUN --mount=type=cache,target=/root/.cache \
pip install "poetry==$POETRY_VERSION"
RUN --mount=type=cache,target=/root/.cache/pypoetry \
poetry install --no-rootCache Mount Parameters Explained
type=cache: Specifies this is a cache mount that persists across builds.target=/root/.cache: Mount point for pip's cache directory when installing Poetry.target=/root/.cache/pypoetry: Mount point for Poetry's cache directory where downloaded dependencies are stored.
For more information regarding Poetry cache mounts, please visit the official Poetry documentation.