Getting started with uv: an existing project, a new project, and the numbers (pip 2.46 s → uv 0.38 s)
uv is a fast Python package and project manager written in Rust. This is the write-up of the video above: dropping uv into a project that already has a requirements.txt, starting a new project from scratch, and a pip-versus-uv benchmark on one machine with the conditions stated. Every number below is on screen in the video and came from the same runs.
The two commands first
- Existing project:
uv pip install -r requirements.txt - New project:
uv init && uv add <pkg> && uv sync
If all you want is faster installs, the first line is the whole migration. The rest of this post is what happens around those two lines, and where to be careful.
Install
One command, one binary. You do not need a Python on the machine to install it, which matters when the thing you are installing is what will manage your Pythons.
curl -LsSf https://astral.sh/uv/install.sh | sh
The video pins version 0.11.6 and installs into a throwaway directory instead:
$ curl -LsSf https://astral.sh/uv/0.11.6/install.sh | UV_INSTALL_DIR=/tmp/uvtest sh downloading uv 0.11.6 aarch64-apple-darwin installing to /tmp/uvtest uv uvx everything's installed! WARN: The following commands are shadowed by other commands in your PATH: uv uvx $ /tmp/uvtest/uv --version uv 0.11.6 (65950801c 2026-04-09 aarch64-apple-darwin)
Why pin an old version? The benchmark ran on 0.11.6, and I wanted every number and every version string on screen to come from one build. Newer releases are faster in places, so read the numbers here as a floor. The "shadowed" warning appears because this Mac already had a uv on the PATH; you will not see it on a clean machine. For your own install, use the first command and take the latest.
Replace pip in a project you already have
Nothing gets converted. uv ships a pip-compatible interface, so the command you already know still works against the same requirements.txt.
$ cat requirements.txt requests rich httpx pydantic click jinja2 pyyaml python-dateutil
Same file installed twice, both with caches turned off:
$ python3 -m venv .venv-pip && time .venv-pip/bin/pip install -q --no-cache-dir --disable-pip-version-check -r requirements.txt real 0m2.643s user 0m1.582s sys 0m0.303s $ uv venv --python 3.14 .venv-uv && time uv pip install -q --no-cache -r requirements.txt --python .venv-uv/bin/python Using CPython 3.14.6 interpreter at: /opt/homebrew/opt/python@3.14/bin/python3.14 Creating virtual environment at: .venv-uv Activate with: source .venv-uv/bin/activate real 0m0.363s user 0m0.153s
Notice what did not happen: no rewritten requirements file, no new project layout, no CI changes.
The numbers, with the conditions
Numbers without conditions are not worth much, so here they are together. Eight packages, caches disabled on both sides, five runs each, median.
Mac mini M4 Pro / macOS 26.6.1 / Python 3.14.6 / pip 26.1.2 / uv 0.11.6 8 packages, caches disabled on both sides, 5 runs each, median install pip 2.457s uv 0.381s 6.4x create venv venv 1.143s uv 0.026s 43.9x ------------------------------------------------------ full flow 3.600s -> 0.407s 8.8x (bonus) uv install with warm cache: 0.062s
Two things worth separating. Installing the packages is the headline, 6.4×. Creating the environment is a separate cost, and that gap is bigger: 1.143 s against 0.026 s. Put together, the thing you actually do went from 3.6 s to 0.4 s.
One caveat: that is a cold install. With a warm cache uv drops to 0.062 s, but comparing a warm run against a cold one would be cheating, so treat that line as a bonus, not the headline.
Update, 2026-09-09: uv 0.12.11 shipped the day before this post went live, so I re-ran the same eight packages, caches off, five runs each, median, on the same machine with both uv versions and pip 26.1.2 back to back:
create venv install full flow
pip 26.1.2 1.202s 2.338s 3.540s
uv 0.11.6 0.150s 0.383s 0.533s
uv 0.12.11 0.136s 0.375s 0.511s
Install is 6.2× faster than pip on 0.12.11 and the full flow 6.9×, so the video's numbers still hold as a floor. The two uv versions are within 4% of each other on this workload. The venv column differs from the table above because this re-run timed uv venv --python 3.14 from a Python harness rather than the shell's time; compare rows within one table, not across the two.
Start a new project: init, add, sync
The other entry point is starting fresh. uv init lays out a project, uv add resolves and installs in one step while writing a lock file, and uv sync rebuilds the environment from that lock file.
$ uv init demo && cd demo && ls
Initialized project `demo` at `.../uvdemo/demo`
main.py pyproject.toml README.md
$ uv add requests rich
Using CPython 3.13.13
Creating virtual environment at: .venv
Resolved 10 packages in 71ms
Prepared 7 packages in 79ms
Installed 9 packages in 10ms
+ certifi==2026.7.22
+ charset-normalizer==3.5.1
+ idna==3.19
+ markdown-it-py==4.2.0
+ mdurl==0.1.2
+ pygments==2.21.0
+ requests==2.34.2
+ rich==15.0.0
+ urllib3==2.7.0
$ cat pyproject.toml
[project]
name = "demo"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"requests>=2.34.2",
"rich>=15.0.0",
]
This is why the lock file exists. Delete the environment completely, run uv sync, and you get the exact same versions back, not whatever resolves today:
$ rm -rf .venv && uv sync Using CPython 3.13.13 Creating virtual environment at: .venv Resolved 10 packages in 0.59ms Installed 9 packages in 7ms + certifi==2026.7.22 + charset-normalizer==3.5.1 + idna==3.19 + markdown-it-py==4.2.0 + mdurl==0.1.2 + pygments==2.21.0 + requests==2.34.2 + rich==15.0.0 ...
pyproject.toml says what the project asks for. uv.lock records the exact resolved graph, transitive packages included. Commit it, and a teammate who clones the repo runs one command and has your environment.
Run without activating
uv run resolves the project's environment, makes sure it is in sync, and runs your command inside it. No source .venv/bin/activate, no shell state to lose.
$ uv run python -c 'import requests; print(requests.__version__)' 2.34.2
The interpreter it used is the project's own, not the system one, without touching anything.
Python versions and tools
uv installs Python itself. Pin a version per project and anyone who runs uv sync gets that interpreter downloaded automatically. No pyenv, no system-Python roulette.
$ uv python list | head -6 cpython-3.15.0a8-macos-aarch64-none <download available> cpython-3.15.0a8+freethreaded-macos-aarch64-none <download available> cpython-3.14.6-macos-aarch64-none /opt/homebrew/bin/python3.14 -> ... cpython-3.14.6-macos-aarch64-none /opt/homebrew/bin/python3 -> ... cpython-3.14.4-macos-aarch64-none <download available> cpython-3.14.4+freethreaded-macos-aarch64-none <download available> $ uv run --python 3.13 python -V Python 3.13.13
It also manages command-line tools. uv tool install puts a command on your PATH for good; uvx runs one once in a throwaway environment. Same package, different commitment.
uv tool install ruff uvx ruff check .
Three things to check before moving a real project
1. The tool directory may not be on your PATH. This happened while making the video. uv tool install httpie succeeded, installed 16 packages and 3 executables, and printed the warning right there:
$ uv tool install httpie Resolved 16 packages in 71ms Prepared 2 packages in 23ms Installed 16 packages in 25ms ... Installed 3 executables: http, httpie, https warning: `/Users/smkim/.local/bin` is not on your PATH. To use installed tools, run `export PATH="/Users/smkim/.local/bin:$PATH"` or `uv tool update-shell`. $ http --version bash: http: command not found
uv did warn. Until the PATH was fixed, the command still would not resolve. Read the last line of the install output.
2. Switching installs is cheap. Switching your team's workflow and CI at the same time is not. The pip-compatible path exists so you can do the first without the second. Take the faster installs today; move the layout, lock file and CI when the team is ready.
3. Private indexes, authenticated registries and unusual build backends are where a drop-in replacement stops being drop-in. It is not that uv cannot do them. It is that "it just works" stops being a safe assumption there, so test before you commit.
What to do today
Point uv at whatever you are working on right now and see what your install time drops to. That is the whole test.
uv pip install -r requirements.txt
Measured on a Mac mini M4 Pro, macOS 26.6.1, Python 3.14.6, pip 26.1.2, uv 0.11.6. The two timings in the "Replace pip" section are single runs from the recording; the table reports five-run medians. They are different runs on purpose, and they agree.
Comments