Why I Replaced Poetry with uv for almost everything
I used to be that person who would smugly paste a poetry install
command into every Dockerfile, CI, etc. I even wrote a blog post last year praising Poetry—it still felt like the obvious default—until one night, after skimming a few posts about uv
and waiting on slow builds after every tiny tweak, I installed it out of curiosity. The build that had been taking two minutes finished in fourteen seconds. 🤯
That single data point made me pause. Over the next few weeks I migrated a side project, then a few repository, and eventually every new workflow on my laptop and I started to apply. Today uv
is the first tool I install on a fresh dev box—right after git
and zsh
.
⚙️ What exactly is uv
?
Consider uv
your all-in-one solution for Python dependency and environment management:
Need… | What I used before | What I use now |
---|---|---|
Resolve & lock dependencies | poetry , pip-tools | uv add / uv lock |
Create venvs | virtualenv | uv (built‑in) |
Manage Python versions | pyenv or system installers | uv python |
Isolated CLI tools | pipx | uv tool & uvx |
One‑off scripts | A Makefile target nobody remembered | Shebang: #! uv python |
Why does it feel faster? The team rewrote the entire dependency solver in Rust and removed a lot of legacy edge‑cases that only matter to 0.1 % of packages. I rarely dive into the implementation details of python dependency tools, but seeing uv sync
finish before the coffee machine warms up still feels like cheating.
TL;DR —
uv
takes the everyday pain points of Python tooling and solves them with a single binary that happens to be lightning‑fast.
🚀 Five‑minute starter guide
I’ve tested these steps on macOS, Ubuntu, and a Windows. They’re all you need to get productive:
# 1 — install
curl -LsSf https://astral.sh/uv/install.sh | sh # macOS / Linux
# iwr https://astral.sh/uv/install.ps1 | iex # Windows PowerShell
# 2 — create a fresh project
uv init ml-exp && cd ml-exp
# 3 — add your libraries
uv add scikit-learn xgboost optuna matplotlib pandas
# 4 — lock & sync for reproducibility
uv lock && uv sync
# 5 — run code inside the managed venv
uv run python train.py
🧪 Where uv
earned its keep in my MLOps/DevOps life
CI pipelines — Switching from Poetry’s dependency install to
uv sync
can reduce each test job by ~6 minutes, adding up to roughly one full engineer-day of CI time saved over 300 runs a week.Dockerized experiments — Swapping
poetry install
foruv sync
inside Dockerfiles shaved minutes off image builds—suddenly, iterating on model containers felt as fast as editing notebooks.Colleague laptops — New hires clone the repo, run
uv sync
, and are ready to code before they’ve even signed in to their email.
🐍 Multi‑python made boring
Even with lightweight projects, hopping between toolchains is friction-free now:
Stack | Why it’s pinned | Python |
---|---|---|
MLflow 2.22.0 + Kedro 0.19.13 | Production pipeline needs fully versioned data sets and reproducible runs on an image that’s still GLIBC 2.31 | 3.10 |
LangChain 0.3.25 + ChromaDB 1.0.12 (RAG playground) | Wheels compiled for the 3.11 ABI; keeps vector-store builds lightning-fast | 3.11 |
Prefect 3.4.3 + FastAPI 0.115.12 (orchestration + micro-service) | Leans on the concurrency and contextvars improvements in 3.12 for async flows | 3.12 |
# one–time interpreter install
uv python install 3.10 3.11 3.12
# slim envs per stack
uv venv pipeline-prod --python 3.10 # MLflow + Kedro
uv venv rag-lab --python 3.11 # LangChain + ChromaDB
uv venv orchestrator --python 3.12 # Prefect + FastAPI
Need to lock a repo? uv python pin 3.11
drops a .python-version
file so your IDE, pre-commit, and CI all agree, no more surprise interpreter conflicts between projects.
💡 Pro tips & common issues
Tip | Why it matters |
---|---|
Commit uv.lock | Your future self (and your CI) will thank you. |
Use uvx for one‑liners | Perfect for ruff or black in pre‑commit hooks. |
uv sync --production | Builds a minimal env excluding dev tools and test libraries. |
Watch for binary wheels | If a package ships only sdist, uv add will tell you before CI fails. |
⚠️ Minor snags worth knowing:
SSH-based Git dependencies — Resolving a private GitHub package over SSH failed silently until I manually exported:
export GIT_SSH_COMMAND='ssh -i ~/.ssh/id_ed25519'
After that,
uv
handled it fine, but the initial error message gave no hint it was SSH-related.No editable installs (
-e
) yet —uv
doesn’t supportpip install -e .
, which is a blocker if you rely on live reload during local development. You’ll need to work around it with manual symlinks or fall back to pip for now.Slight CLI surprises — If you run
uv pip install ...
outside auv
-managed venv, it installs into your global interpreter, unlikepip
, which often warns or errors. It’s easy to forget you’re not in a managed shell, so double-check where you’re installing.
⚔️ Head‑to‑head speed numbers
Scenario | uv | PDM | Poetry | pip-sync / pip-compile |
---|---|---|---|---|
Warm Installation | 0.1 s | 1.8 s | 0.9 s | 3.6 s (pip-sync) |
Cold Installation | 1.2 s | 2.1 s | 2.7 s | 7.1 s (pip-sync) |
Warm Resolution | 0.03 s | 2.0 s | 0.6 s | 1.2 s (pip-compile) |
Cold Resolution | 0.4 s | 3.2 s | 3.5 s | 3.8 s (pip-compile) |
uv = quickest result in each benchmark; bold values mark where uv
beats every other tool by a wide margin.
What each scenario means
- Warm Installation – Re-installing with wheels already cached locally.
- Cold Installation – First-time install on a clean cache.
- Warm Resolution – Updating/locking deps after a previous successful run.
- Cold Resolution – Resolving a lock file on a brand-new machine with no cached metadata.
Where to find the raw benchmark
Check out their GitHub benchmarks here for more details.
✅ When to reach for uv
, and when not to
uv
is a no‑brainer for:
- Heterogeneous data‑science repos where setup time kills flow.
- CI pipelines that pay per minute.
- Teams who secretly hate reading internal Confluence pages about
pyenv
hacks.
Hold off if:
- You package a public library and rely on Poetry’s
build
command (for now; UV build is on their roadmap). - You need editable installs (
pip install -e
) with custom PEP 660 backends. support is experimental.
🔗 Further reading & watching
- The concise official docs are surprisingly good.
- Astral’s launch post explains the design philosophy.
Final thoughts
Every few years the Python ecosystem gets a ‘why didn’t we have this earlier?’ moment—black
, then ruff
, and now uv
. Swapping it in took a lazy Saturday afternoon and eliminated half a dozen lines from my Makefile. If your workflow still juggles pip
, venv
, pyenv
, and a dash of pipx
, give uv
an hour. Chances are you’ll save many more.