Install
# Linux / macOS
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# Or via Homebrew
brew install uv
# Or as a Python package (bootstraps the binary)
pipx install uv
uv is a single self-contained binary — no system Python required to install it. After install:
uv --version
uv self update # uv updates itself
Project workflow
The core commands are uv init, uv add, uv sync, uv run. They mirror what poetry/cargo do, but operate on a standard pyproject.toml with no proprietary sections.
# Create a new project
uv init my-app
cd my-app
# Add dependencies
uv add fastapi uvicorn
uv add --dev pytest ruff mypy
# Run a command inside the project's managed environment
uv run python -m my_app
uv run pytest
# Reproduce the locked environment from scratch
uv sync
uv init creates a pyproject.toml, a .python-version, a project skeleton, and a .venv when needed. uv add updates pyproject.toml AND uv.lock AND the venv in one transaction. uv run ensures the venv is up to date before executing the command — equivalent to poetry run but with no separate activate step.
Lockfiles, properly cross-platform
uv.lock records resolved versions and hashes for every supported platform (Linux x86_64, Linux aarch64, macOS, Windows) in a single file. A teammate on a different OS can uv sync the same lockfile and get a deterministic install on their platform — no separate per-platform resolution.
uv lock # re-resolve all dependencies, write uv.lock
uv lock --upgrade # also bump within constraints
uv lock --upgrade-package fastapi
uv tree # show the resolved dependency tree
Python version management
uv handles Python interpreters too — pyenv is no longer needed:
uv python list # available versions (installed and downloadable)
uv python install 3.12 3.13 # download & cache specific versions
uv python pin 3.13 # write .python-version for this project
uv venv --python 3.11 # create a venv with a specific version
Interpreters land under ~/.local/share/uv/python/ and are reused across projects. The downloads are Astral's portable-Python builds — relocatable, statically linked enough to run without system OpenSSL, signed.
pipx replacement: uv tool
uv tool is uv's equivalent of pipx — install Python applications into isolated environments and expose their entry points on PATH:
uv tool install ruff
uv tool install black
# One-shot: run a tool without installing it
uvx ruff check .
uvx black .
uvx is shorthand for uv tool run. The first invocation downloads the tool's environment; subsequent runs are cache hits and start instantly.
Inline-metadata scripts (PEP 723)
A single-file Python script can declare its dependencies inline:
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "httpx",
# "rich",
# ]
# ///
import httpx
from rich import print
print(httpx.get("https://example.com").status_code)
uv run script.py reads the metadata block, materializes a venv with the declared dependencies (cached for reuse), and runs the script. This collapses the "tiny script that needs three packages" friction to zero — no virtualenv, no pip install, no requirements.txt.
Migrating from pip / requirements.txt
uv accepts requirements.txt directly as a compatibility surface:
uv pip install -r requirements.txt
uv pip compile pyproject.toml -o requirements.txt
uv pip sync requirements.txt # ensure venv exactly matches requirements.txt
uv pip is uv's pip-compatible CLI — same commands and flags, just faster. For projects that don't yet have a pyproject.toml, this is the minimum-disruption path. The full project workflow (uv add/uv sync/uv.lock) is the long-term destination.
Migrating from poetry
Most poetry projects already have a usable pyproject.toml. Two changes:
- Move the
[tool.poetry]section to the standard[project]table (PEP 621). Dependencies move from[tool.poetry.dependencies]to[project] dependencies = [...]. - Delete
poetry.lock, runuv lockto generateuv.lock.
uv keeps poetry-style dev groups via [dependency-groups] (PEP 735):
[dependency-groups]
dev = [
"pytest>=8",
"ruff",
"mypy",
]
uv add --group dev pytest
uv sync --group dev # install runtime + dev
uv sync # install runtime only
In CI
The official GitHub Action handles caching and version pinning:
- uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- run: uv sync --frozen
- run: uv run pytest
uv sync --frozen errors if uv.lock would change — the equivalent of npm ci. Always use it in CI; never use plain uv sync there (which would silently re-resolve).
What it doesn't (yet) replace
Build backends: uv invokes the project's build backend (hatchling, setuptools, flit, maturin) for building wheels; it isn't a build backend itself. Pick a separate one in [build-system]. Hatchling is a reasonable default for pure-Python packages; maturin if there's a Rust extension.
For most everyday workflow — install, lock, run, manage Python versions, run dev tools, ship to CI — uv covers it. Once a project is on uv, the local toolchain is a single binary instead of pip + pipx + pyenv + poetry + virtualenv, and that single binary is dramatically faster than the sum of those.