# Optimal Dockerfile for Python with poetry (https://depot.dev/docs/container-builds/optimal-dockerfiles/python-poetry-dockerfile)

<NoteCallout>
  **Looking for faster Python builds?** We recommend using [<u>UV</u>](./python-uv-dockerfile) 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.
</NoteCallout>

Below is an example `Dockerfile` that we recommend at Depot for building Docker images for Python applications that use `poetry` as their package manager.

```dockerfile
# 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`

```dockerfile
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.1` pins the Poetry version for reproducible builds
* `POETRY_VIRTUALENVS_IN_PROJECT=true` creates virtual environments inside the project
* `POETRY_NO_INTERACTION=1` disables interactive prompts
* `PYTHONUNBUFFERED=1` ensures logs are output in real-time

#### Installing Poetry

```dockerfile
RUN --mount=type=cache,target=/root/.cache \
    pip install "poetry==$POETRY_VERSION"
```

We install Poetry using pip with cache mounts for efficiency.

#### Dependency installation

```dockerfile
WORKDIR $PYSETUP_PATH

COPY poetry.lock pyproject.toml ./

RUN --mount=type=cache,target=/root/.cache/pypoetry \
    poetry install --no-root
```

We copy the Poetry configuration files and install dependencies without installing the project itself first.

#### Source code installation

```dockerfile
COPY . .
```

After dependencies are installed, we copy the source code.

### Stage 2: `FROM python:3.13-slim AS runtime`

```dockerfile
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:

```dockerfile
RUN --mount=type=cache,target=/root/.cache \
    pip install "poetry==$POETRY_VERSION"

RUN --mount=type=cache,target=/root/.cache/pypoetry \
    poetry install --no-root
```

### Cache 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](https://python-poetry.org/docs/configuration/#cache-dir).

## For AI Agents

The full site index is at [llms.txt](https://depot.dev/llms.txt). Append `.md` to any documentation, blog, changelog, or customer URL to fetch its markdown source directly.