Multi-Stage Docker Builds for Python Projects using uv

Written by Alexander Kammerer on (last updated on )

Charlie Marsh and the brillant guys at Astral have helped the python ecosystem a lot with ruff and now they have released a new tool: uv.

It is intended as a drop-in replacement of pip. At the moment, it supports dependency resolution and installation. Compared to pip, it is a lot faster. I have observed up to 10-15x faster dependency installations (with no caching) for medium-sized projects.

Using uv to speed up Docker builds

Another nice benefit is that his can speed up your Docker build. You can even use uv without having to install it into your final Docker image using a multi-stage build.

Multi-stage builds

A typical multi-stage build for a Python projects works like this:

Example: Azure Function App

Let’s look at this example of a Dockerfile for an Azure Function App.

# syntax = docker/dockerfile:1.0-experimental

# Build image to compile all packages
FROM python:3.11 as build

ENV VIRTUAL_ENV=/home/packages/.venv
RUN chmod -R 655 / && / && rm /

COPY ./requirements.txt .
RUN /root/.cargo/bin/uv venv /home/packages/.venv
RUN /root/.cargo/bin/uv pip install --no-cache -r requirements.txt

# Run image for the function app code

ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true \
    AzureWebJobsFeatureFlags=EnableWorkerIndexing \
    PATH="/home/packages/.venv/bin:$PATH" \

COPY --from=build /home/packages/.venv /home/packages/.venv
COPY . /home/site/wwwroot

(Thank you to @ryxcommar for the Dockerfile commands that install uv from his blog article.)

Let’s break this down assuming you have your Function App files in the directory for which you are calling docker buildx build .:

This is it for the build image. Now, we need a final image:

Example: Python app

A more general example for a Dockerfile of a Python app could be this:

# syntax = docker/dockerfile:1.0-experimental

# Base image
FROM python:3.12 as build

RUN apt-get update && apt-get install -y build-essential curl
ENV VIRTUAL_ENV=/opt/venv \

RUN chmod -R 655 / && / && rm /
COPY ./requirements.txt .
RUN /root/.cargo/bin/uv venv /opt/venv && \
    /root/.cargo/bin/uv pip install --no-cache -r requirements.txt

# App image
FROM python:3.12-slim-bookworm
COPY --from=build /opt/venv /opt/venv

# Activate the virtualenv in the container
# See here for more information:
ENV PATH="/opt/venv/bin:$PATH"

In this example, we use the python:3.12 image as the build image and then the slimmed down version python:3.12-slim-bookworm for the final app image.

You will need to add your ENTRYPOINT to make this work but the dependencies will be installed and calling python will use your virtual environment.

If you want to learn more about multi-stage docker builds, I can highly recommend this article about multi-stage docker builds for python and also this post about using virtual environments in dockerfiles from Itamar Turner-Trauring.