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.
# 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"]
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.
FROM python:3.13-slim AS build
)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 startupUV_LINK_MODE=copy
: Ensures uv copies files instead of creating symlinksCOPY 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.
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.
FROM python:3.13-slim AS runtime
)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.
Cache mounts are one of the most powerful features for optimizing Docker builds with Depot. This Dockerfile uses the following cache mount syntax:
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --no-install-project --no-dev
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.