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

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

```dockerfile
# syntax=docker/dockerfile:1

FROM python:3.13-slim AS build

COPY --from=ghcr.io/astral-sh/uv:0.8.21 /uv /uvx /bin/

WORKDIR /app

ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy

COPY uv.lock pyproject.toml ./

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --no-install-project --no-dev

COPY . .

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --frozen --no-dev

FROM python:3.13-slim AS runtime

ENV PATH="/app/.venv/bin:$PATH"

RUN groupadd -g 1001 appgroup && \
    useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser

WORKDIR /app

COPY --from=build --chown=appuser:appgroup /app .

USER appuser

ENTRYPOINT ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

## Explanation of the Dockerfile

Using a multi-stage build, we can separate our build from our deployment, taking full advantage of Docker's layer caching to speed up our builds and produce a smaller final image.

### Stage 1: Build Stage (`FROM python:3.13-slim AS build`)

```dockerfile
FROM python:3.13-slim AS build

COPY --from=ghcr.io/astral-sh/uv:0.8.21 /uv /uvx /bin/

WORKDIR /app

ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
```

We use Python 3.13 slim for a smaller base image. We copy the uv binary from the official uv container image, which is more efficient than installing it via pip.

Key environment variables:

* `UV_COMPILE_BYTECODE=1`: Tells uv to compile Python files to bytecode for faster startup
* `UV_LINK_MODE=copy`: Ensures uv copies files instead of creating symlinks

#### Dependency installation

```dockerfile
COPY uv.lock pyproject.toml ./

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --no-install-project --no-dev
```

First, we copy the lock file and project configuration, then install dependencies without the project itself. This layer caches dependencies separately from application code.

#### Project installation

```dockerfile
COPY . .

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --frozen --no-dev
```

After copying the full application, we install the project itself using the frozen lock file to ensure reproducible builds.

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

```dockerfile
FROM python:3.13-slim AS runtime

ENV PATH="/app/.venv/bin:$PATH"

RUN groupadd -g 1001 appgroup && \
    useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser

WORKDIR /app

COPY --from=build --chown=appuser:appgroup /app .

USER appuser

ENTRYPOINT ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

The runtime stage uses a clean slim image and creates a non-root user for security. We copy the entire application including the virtual environment 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 the following cache mount syntax:

```dockerfile
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --no-install-project --no-dev
```

### Cache Mount Parameters Explained

* **`type=cache`**: Specifies this is a cache mount that persists across builds.
* **`target=/root/.cache/uv`**: The mount point for uv's cache directory where downloaded packages and compiled wheels are stored.

For more information regarding uv cache mounts, please visit the official [uv documentation](https://docs.astral.sh/uv/concepts/cache/#cache-directory).

## 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.