uv: The Fast Python Package Manager Worth Switching To
uv replaces pip, virtualenv, pip-tools, and more in one Rust-powered tool. Learn how to install, manage dependencies, and run Python projects with uv.
Managing Python environments used to mean juggling four or five separate tools — pip to install packages, venv or virtualenv to isolate them, pip-tools to compile lockfiles, pyenv to manage Python versions, and pipx to install command-line tools globally. uv, built by Astral (the team behind the Ruff linter), collapses all of that into a single binary, and it does so at a speed that makes the old way feel sluggish by comparison.
What uv replaces
uv is not a wrapper around existing tools. It is a ground-up reimplementation in Rust that targets the same use cases across the Python workflow:
| Old tool | What uv replaces it with |
|---|---|
pip | uv pip install, uv pip compile |
pip-tools | uv pip compile / uv lock |
virtualenv / venv | uv venv |
pyenv | uv python install / uv python pin |
pipx | uvx / uv tool run |
| Poetry / PDM (project workflow) | uv init, uv add, uv sync |
Not every use case is identical — uv is not trying to replicate Poetry’s publishing workflow, for instance — but for dependency management, lockfiles, environments, and tool running, the coverage is thorough.
Why it’s fast
Two architectural choices drive the speed:
Written in Rust. Dependency resolution and package I/O in Python tooling have historically been done in Python itself. A Rust implementation removes a large constant-factor overhead, especially for resolution algorithms that need to walk large dependency graphs.
Global content-addressable cache. uv caches packages by content hash globally across your machine, not per-project. Once a version of a package has been downloaded, it is never downloaded again — even in a fresh virtual environment. Parallel network requests and concurrent extraction compound this further. The net result is that cold installs often land an order of magnitude or more faster than pip for typical project sizes, and warm installs approach near-instant.
Step 1: Install uv
The quickest route on macOS and Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
On Windows, use the PowerShell installer from the uv docs. Once installed, verify with:
uv --version
No separate Python installation is required to run uv itself. However, if you do not have Python on your system, uv can fetch it for you (covered below).
Step 2: Start a new project
uv init scaffolds a project with a pyproject.toml and a hello.py placeholder:
uv init my-project
cd my-project
The generated pyproject.toml follows the modern PEP 517/518 format. uv does not invent a proprietary format — it writes and reads standard Python project metadata.
Step 3: Add dependencies
uv add requests httpx
This does three things at once: it resolves the dependency graph, installs into the project’s virtual environment (creating one if it does not exist), and writes a uv.lock file. The lockfile captures the exact resolved versions of every package in the full dependency tree — direct dependencies and transitive alike — so that uv sync on another machine produces an identical environment.
To add a development-only dependency:
uv add --dev pytest ruff
To remove a package:
uv remove requests
Step 4: Sync the environment
On a fresh clone, or after pulling changes that updated uv.lock:
uv sync
This brings the virtual environment exactly into alignment with the lockfile. No more “works on my machine” environment drift. The pattern should feel familiar if you have used Docker for environment reproducibility — the lockfile plays the role the Dockerfile plays in Docker for beginners.
Step 5: Run code without activating the environment
uv run python my_script.py
uv run automatically uses the project’s virtual environment. You can also run a module:
uv run pytest
uv run python -m http.server
This is useful in scripts and CI pipelines where you want to avoid sourcing activation scripts explicitly.
Step 6: Manage virtual environments directly
If you need to manage environments outside of a project context — for example, in a monorepo or a one-off script:
uv venv .venv
source .venv/bin/activate
uv pip install numpy pandas
The uv pip sub-commands are intentionally pip-compatible. You can swap uv pip install for pip install in most contexts with no changes to your mental model. This is the lowest-friction migration path for teams that want to speed up installs without rethinking their entire workflow.
Step 7: Install and manage Python versions
uv python install 3.12
uv python install 3.11 3.10
uv downloads and manages Python interpreters independently of your system Python. To tell a project which version to use:
uv python pin 3.12
This writes a .python-version file that uv respects when creating environments. No more maintaining a separate pyenv or dealing with system Python collisions.
Step 8: Run CLI tools without installing them
uvx (alias for uv tool run) lets you run a Python CLI tool ephemerally — it is downloaded, run, and discarded, never polluting your global or project environment:
uvx ruff check .
uvx black --check my_script.py
uvx cowsay hello
For tools you use regularly and want to keep available globally:
uv tool install ruff
uv tool install black
Installed tools are accessible on your PATH and managed by uv, cleanly separate from any project environments — the role pipx has traditionally filled.
The pyproject.toml + uv.lock workflow
The recommended workflow for any collaborative project is:
- Commit both
pyproject.tomlanduv.lockto version control. - Run
uv syncto reproduce the environment on any machine. - Run
uv add/uv removeto change dependencies — never edit the lockfile by hand.
The lockfile is designed to be human-readable and to produce minimal diffs when dependencies change. This is the same philosophy that Bun’s lockfile and npm’s package-lock.json follow — the Bun vs Node comparison is worth reading if you want a parallel picture of how JavaScript tooling has consolidated in the same direction.
Adopting uv incrementally
You do not need to migrate your whole workflow at once. The uv pip interface means you can start with:
uv pip install -r requirements.txt
…and see the speed difference immediately. From there, you can adopt uv sync and uv.lock when you are ready. uv is designed so that each piece can be adopted independently.
The takeaway
uv’s pitch is simple: one tool, one cache, dramatically faster installs, and a first-class lockfile workflow that makes reproducible Python environments easy to maintain. The Rust foundation is an implementation detail that produces a genuinely better developer experience. Whether you are starting a new project or looking to speed up your CI pipeline, uv is the Python package manager worth adopting now.
Tagged
Keep reading
Takina · · 7 min read Rust Adopts LLM Policy: What's Allowed for AI Code
Five rust-lang/rust teams ratified an LLM policy: models can analyze and review, but not author contributions. Here's what's permitted, banned, and why.
Chisato · · 4 min read What Is Typosquatting? The Package Ecosystem's Silent Trap
Typosquatting publishes malicious packages under names that look like popular ones, hoping developers mistype an install command. How it works.
Chisato · · 5 min read Build Your Own Redis in 200 Lines of Python
Build a Redis clone in Python that the real redis-cli can talk to — a TCP server, the RESP protocol, key expiry, and an in-memory store in under 200 lines.