DEV Community

George Shuklin
George Shuklin

Posted on

from poetry to uv

I've done my fourth project conversion from Poetry (or a plain pip) to uv.

Results: absolutely zero issues.

Moreover, I have an interesting imrovement. The real reproducible CI image (debian with terraform, just, ansible, sops).

Sizes for tar files for a Docker image:

- poetry (ci image size 264M, build time 2:17-2:26) + uv (ci image size 235M, build time 1:30-1:50) 
Enter fullscreen mode Exit fullscreen mode

A Python application takes additional ~30MB more in archived state. Why? Because of dependencies. A lot of them. Uv is not small (44MB, but Python one is enormous).

Also, you can see how faster image build become. Most of the build time is Ansible collection installation time (I can't reduce that one), but installation time for all python stack (mostly Ansible and it's dependencies) shrunk from significant to negligible.

Build process spend now more time on apt than on python packages. With poetry and pip, those times were comparable.

I identified two different ways to use uv in the CI image: with uv and without.

If uv is present in the image, you need install it, and you need pyproject.toml and uv.lock files.

If uv is not used, you need to use some other process (usually, the operator) to export dependencies into requirements.txt (with hashes) via uv export. And you can skip copying pyproject.toml file completely.

After trying both ways I more inclined to use with uv (because it's faster, way, way faster). It also removes hassle for creating venv as separate comand. A simple uv sync do the job.

Also, in with uv I can skip python3-pip package, which alleviate some of the size price from adding uv.

Three projects I migrated by hands.

On my fourth project I found even faster way to do it.

uvx migrate-to-uv 
Enter fullscreen mode Exit fullscreen mode

It does not exclude CI work (images, etc), but reduce cognitive load on converting pyproject.toml.

... Why not poetry?

Even without 'speed' factor I got two big issues with poetry.

  1. They started well, got into modern distributions (apt-get install python3-poetry), but they broke compatibility in 1.6-2.0, in such a way, that you can't have pyproject.toml been compatible with both. It's moving too fast and breaks too many things to be a packet manager.

  2. I found odd situation that it flips random write bits on installed packets. I build reproducible images, so I have runs, where diffoscope see something odd, like 0754 in one image and 0757 in another. I rune out everything but poetry, and poetry does this. Maybe I should file a bug for this? But I have no definitive proofs (it's probabilistic, random file at random run), and I know how much time I would spend reporting this thing. I already lost few days of my work time nailing problem down. Instead I move from poetry.

  3. There was time when it was possible to use poetry without poetry (poetry export) but they decided it's too easy to use and moved into separate package, deprecating and removing function from the main package. They have reasons. To make things more complex than they should be. I have reasons. To move on.

All this leads to 'should we try uv?', and I tried. Project #1 was simple and migration was trivial. Project #2 was simple and migration was okay (because of CI nuances and internal policies on artifacts). Project #3 (where I tried uv export was okay, but I had to add an additional job to check if uv export is not stale. I didn't liked it. Project #4 was converted in less than hour (including Hadolint nitpicking), and it was straightforward and simple.

And it was reproducible from the first attempt. No more odd caches rm at the end of stage (poetry still leaves some randomness even with --no-cache), just simple uv sync --no-cache.

What I miss is a docker image with uv. Writing 'copy --from' in the Dockerfile is much nicer than my current UV downloading code:

 && bash -c "curl -L https://github.com/astral-sh/uv/releases/download/0.7.16/uv-x86_64-unknown-linux-musl.tar.gz | tar xz --wildcards */uv" \ && mv uv-x86_64-unknown-linux-musl/uv /usr/local/bin \ && echo 6538516743f9c33aec8e53398730dcc55c6bbcbe2f660a7757c10d358b85e675 /usr/local/bin/uv | sha256sum -c \ 
Enter fullscreen mode Exit fullscreen mode

compare to my TF code:

# from hashicorp/terraform:1.12 at 2025-06-22 FROM hashicorp/terraform@sha256:f5ac787eee9d292b6a3b97d40f04019ce08189d356233fc73d5ec7ef8529cce2 as terraform ... COPY --from=terraform /bin/terraform /usr/local/bin/ 
Enter fullscreen mode Exit fullscreen mode

(Yep, price for hash pinning is high).

Top comments (0)