March 4, 2024 update: This blog has been updated to reflect additions to uv as of 0.1.12, specifically the --system
flag. See Addendum section for more information.
When I heard that Charlie Marsh, the creator of Ruff, created a fast replacement for pip called uv
, I dropped everything I was doing and added it to all my repos. It’s great. It cut my build times in half. You should be using it in all your projects.
Here’s a TLDR of how to use it in Docker, assuming you are using the Python base image.
Imagine you have the following Dockerfile
:
FROM python:3.12
COPY requirements.txt /requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
COPY hello.py /hello.py
CMD ["python", "hello.py"]
Here is what it looks like with uv
:
FROM python:3.12
ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh
RUN /install.sh && rm /install.sh
COPY requirements.txt /requirements.txt
RUN /root/.cargo/bin/uv pip install --system --no-cache -r requirements.txt
COPY hello.py /hello.py
CMD ["python", "hello.py"]
And there you go.
Explanation of the choices in the code:
ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh
and thenRUN /install.sh && rm /install.sh
installsuv
at the location/root/.cargo/bin/uv
.- You can also
pip install --no-cache-dir "uv~=0.1"
instead ofcurl
orADD
if you want, but in testing thecurl
command is about 2-3 seconds faster. - Why not “
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
“? Because this does not install updates whenuv
is updated. In a worst case scenario whenuv
releases a 1.0 with a breaking change, this can cause your local builds to pass (since it’s using a cached old version) while your remote builds fail.ADD
is better assurance that your local and remote builds will stay in sync, also you get to keep your version ofuv
updated and that’s most likely not a bad thing. If you’d like to keep using the cache, then you can doRUN curl
instead ofADD
.
- You can also
- You want to install
uv
near the top to take advantage of layer caching. Similarly, for layering reasons, you want to pip install beforeCOPY
ing the Python code.
Have fun! And be sure to than Charlie Marsh and major contributors Jacob Finkelman and Matthieu Pizenberg for their incredible work in making the Python ecosystem so much better!
Addendum (March 4, 2024)
A previous version of this blog post, corresponding with uv versions 0.1.0 through 0.1.11, suggested using the following:
FROM python:3.12
ENV VIRTUAL_ENV=/usr/local
ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh
RUN /install.sh && rm /install.sh
COPY requirements.txt /requirements.txt
RUN /root/.cargo/bin/uv pip install --no-cache -r requirements.txt
COPY hello.py /hello.py
CMD ["python", "hello.py"]
With the following blurb about why the VIRTUAL_ENV
environment variable was included:
What’s up with the
VIRTUAL_ENV
environment variable? Long story short, the Python executable for the Python base image is located at/usr/local/bin/python
.uv
is insistent on users utilizing venvs, but you can bypass this by just defining your “venv” to be where the global Python installation is inside the Docker image.uv
recognizes theVIRTUAL_ENV
env var internally.
With uv version 0.1.12, this approach is no longer necessary due to the --system
flag.
This blog post is mostly a relic of the literal first few hours after uv came out. This post was originally written to provide an optimal Docker installation for uv 0.1.0, since it was not clear in 0.1.0 how to best install and use uv in a Dockerfile, and I was so excited for the project that I wanted to make sure everyone had access to it for their builds. The maintainers work incredibly quickly, and have made this a lot easier in 0.12.0, so this blog post is a little less necessary. Still, I feel compelled to keep this up to date, in case anyone else stumbles upon this post.