uv: A Complete Guide to Python's Fastest Package Manager
uv is a fast, all-in-one command-line tool for Python development. Built by OpenAI, it manages Python interpreters, virtual environments, dependencies, lockfiles, and command-line tools from a single binary, replacing pip, pyenv, pipx, virtualenv, and pip-tools. It installs packages several times faster than pip and creates virtual environments in milliseconds.
Note
Already familiar with uv and looking for a quick command reference? See the uv reference page.
What uv does
uv ships as a standalone binary with no Python dependency, so you can bootstrap an entire Python development environment from scratch. One install, one CLI, one mental model. It comes from OpenAI, the same team behind Ruff and ty.
uv achieves large speedups over pip through aggressive caching, parallel downloads, and an optimized dependency resolver written in Rust. The speed comparison shows the gap: installing 23 packages from a warm cache takes pip 6.6 seconds and uv 0.12 seconds.
uv aligns with modern Python packaging standards. It uses pyproject.toml for project metadata following PEP 621, generates cross-platform lockfiles, and works with standard build backends. Projects created with uv remain compatible with pip and other Python tools.
Before uv, the same set of capabilities required combining pyenv for interpreter management, virtualenv for environment isolation, pip-tools for dependency locking, and pipx for CLI tools. Poetry unified some of these but historically used non-standard metadata (Poetry 2.0, released January 2025, added PEP 621 support) and has no built-in Python version management.
For a deeper look at why this fragmentation happened and how uv fits in, see Why are there so many Python packaging tools? and Why you should try uv if you use Python.
How fast is uv?
The speed claims are easy to verify. Here are timed comparisons of uv 0.11.18 against pip 26.0 on the same machine (Apple Silicon, Python 3.14).
Virtual environment creation
python -m venv takes a little over a second. uv venv finishes in about 10 milliseconds.
$ time python -m venv .venv
python -m venv .venv 0.96s user 0.16s system 1.150 total
$ time uv venv .venv
Using CPython 3.14.4
Creating virtual environment at: .venv
uv venv .venv 0.00s user 0.00s system 0.008 total
Installing packages (cold cache)
With no cached wheels, installing five libraries (boto3, requests, pandas, fastapi, sqlalchemy) and their 23 total dependencies:
$ time pip install boto3 requests pandas fastapi sqlalchemy
...
Successfully installed 23 packages
pip install 5.10s user 1.21s system 8.889 total
$ time uv pip install boto3 requests pandas fastapi sqlalchemy
Resolved 23 packages in 298ms
Prepared 23 packages in 797ms
Installed 23 packages in 53ms
uv pip install 0.29s user 0.58s system 1.187 total
pip: 8.9 seconds. uv: 1.2 seconds. About 7x faster on a cold cache, where both tools spend most of their time downloading wheels over the network.
Reinstalling packages (warm cache)
The warm-cache scenario reflects CI pipelines where layers are cached or local development where you recreate an environment:
$ time pip install boto3 requests pandas fastapi sqlalchemy
...
Successfully installed 23 packages
pip install 4.91s user 0.99s system 6.591 total
$ time uv pip install boto3 requests pandas fastapi sqlalchemy
Resolved 23 packages in 5ms
Installed 23 packages in 78ms
uv pip install 0.03s user 0.13s system 0.116 total
pip: 6.6 seconds. uv: 0.12 seconds. Roughly 55x faster. uv’s cache stores pre-built wheels and uses hard links to avoid copying files into the virtual environment, so “installing” cached packages is nearly instantaneous.
Summary
| Operation | pip | uv | Speedup |
|---|---|---|---|
| Create virtual environment | 1.15s | 0.01s | ~115x |
| Install 23 packages (cold cache) | 8.9s | 1.2s | ~7x |
| Install 23 packages (warm cache) | 6.6s | 0.12s | ~55x |
These numbers come from a single machine. Results will vary by hardware, network speed, and package size. The cold-cache gap is smaller because both tools are bottlenecked on the network; the warm-cache and environment-creation gaps show where uv’s parallel downloads, compiled resolver, and hard-link caching pay off. The pattern holds across environments.
Installation
uv ships as a standalone binary with no Python dependency. For full instructions, see How to install uv. The recommended approach is the official installer:
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"Alternative methods:
# Homebrew (macOS)
brew install uv
# pip (if Python is already installed)
pip install uvAfter installing, confirm it works:
uv --versionPlatform-specific details are covered in the dedicated how-to guides for macOS, Linux, and Windows. To keep uv current, see How to upgrade uv.
Core workflows
Managing Python versions
uv can install and manage multiple Python interpreters without pyenv or system package managers.
# Install a specific Python version
uv python install 3.12
# List available versions
uv python list
# Install multiple versions for testing
uv python install 3.11 3.12 3.13Tip
uv run and uv sync automatically download the Python version specified in your pyproject.toml if it isn’t already installed. Every developer gets the same interpreter version without manual setup.
Unlike pyenv, which compiles Python from source (a process that can take minutes and requires build dependencies), uv downloads prebuilt binaries. uv python install finishes in seconds. uv stores these interpreters in a shared cache, so multiple projects using the same Python version share a single installation.
You can pin a project to a specific Python version with a .python-version file:
uv python pin 3.12This writes a .python-version file that uv (and other tools) respect. When a collaborator clones the project and runs uv sync, uv reads this file and installs the correct interpreter automatically.
To make a uv-installed Python available outside of uv projects (for example, so python3 resolves to it in your shell), see How to add Python to your system path with uv.
For a side-by-side comparison with pyenv, read How do pyenv and uv compare for Python interpreter management?. If you are currently using pyenv, the guide How to switch from pyenv to uv walks through the migration step by step.
Creating and managing projects
uv init scaffolds a new project with a pyproject.toml. The virtual environment and lockfile are created when you first run uv sync or uv add.
# Create a new project
uv init my-project
cd my-project
# Add dependencies
uv add requests
uv add pandas numpy
# Add development dependencies
uv add --dev pytest ruff
# Install everything (creates/updates .venv and uv.lock)
uv sync
# Run a command inside the project environment
uv run python main.pyNote
uv.lock pins every direct and transitive dependency to an exact version, ensuring reproducible installs across machines. Unlike pip freeze output, uv’s lockfile is cross-platform by default.
uv supports two project types out of the box: applications and libraries. An application (the default) suits web services, scripts, and data pipelines. A library is intended for packages you publish to PyPI. The difference affects how uv generates your pyproject.toml and how it handles version constraints. Read Understanding uv init project types for guidance on which to choose.
# Create a library project
uv init --lib my-package
# Create an application (default)
uv init my-appuv also handles optional dependencies and dependency groups, letting you define sets of extras like [project.optional-dependencies] for features or [dependency-groups] for development tools. For the mechanics of installing groups with uv sync --group, see Understanding dependency groups in uv.
For a hands-on walkthrough of creating a project from scratch, follow the tutorial Create your first Python project. For libraries with native extensions, see Build a Python library with a C extension or Build a Python library with a Rust extension.
Changing your project’s Python version is straightforward:
uv python pin 3.13
uv syncSee How to change the Python version of a uv project for details.
Lockfiles are central to reproducible environments. uv.lock pins every transitive dependency to an exact version and records platform-specific resolution so that uv sync produces identical environments on macOS, Linux, and Windows.
For more on how lockfiles work and why they matter, see What is a lockfile? and How to use a uv lockfile for reproducible Python environments.
Running scripts
uv run executes a Python script after ensuring the project’s virtual environment is up to date. For one-off scripts that aren’t part of a project, you can specify dependencies inline using PEP 723 metadata:
# /// script
# dependencies = ["requests", "rich"]
# requires-python = ">=3.11"
# ///
import requests
from rich import print
response = requests.get("https://api.github.com/zen")
print(response.text)Run it with:
uv run script.pyuv reads the inline metadata, creates a temporary environment with the declared dependencies, and executes the script. No project setup, no manual virtualenv. The environment is cached, so running the same script a second time skips dependency installation entirely.
You can also pass dependencies on the command line without modifying the script:
uv run --with requests script.pyThis is useful for quick experiments or when running someone else’s script with an additional package. You can even specify a Python version:
uv run --python 3.11 script.pyFor interactive work, uv launches a REPL with your project’s dependencies available:
uv run pythonSee How to run a Python REPL with uv, How to run the IPython shell in your uv project, and How to run a Jupyter Notebook with uv. For test suites, How to run tests using uv covers pytest configuration and common workflows.
Managing CLI tools
Many Python packages ship command-line tools: Ruff, Black, Jupyter, and dozens more. uv provides two ways to run them:
uvx runs a tool in a temporary, isolated environment. No installation required. The tool is cached for fast subsequent runs:
uvx ruff check .
uvx black --check .uv tool install installs a tool permanently so it’s available as a regular command:
uv tool install ruff
ruff check . # now available directlyThis replaces pipx for most use cases. Each tool gets its own isolated environment, so tools never conflict with each other or with your project’s dependencies.
You can also upgrade installed tools:
uv tool upgrade ruff
uv tool upgrade --allThe distinction between uvx and uv tool install maps to how you use the tool. If you run it occasionally or want to try it once, use uvx. If you use it daily and want it on your PATH, use uv tool install. Both approaches isolate the tool’s dependencies from your project.
For a detailed comparison of when to use uv run versus uvx, see When to use uv run vs uvx.
Formatting code
uv includes a uv format command that formats Python code using Ruff’s formatter. This follows the same pattern as cargo fmt in Rust: the formatter is a separate tool under the hood, but uv provides a convenient interface so you don’t need to think about it as a separate dependency. uv downloads and caches the Ruff version it needs the first time you run it.
# Format all Python files in the project
uv format
# Check formatting without making changes
uv format --check
# Show a diff of what would change
uv format --diffFormatting settings are read from [tool.ruff.format] in your pyproject.toml. If you already use Ruff for formatting, uv format uses the same configuration.
Note
uv format is experimental as of uv 0.11.18 and prints a preview warning. Its interface may change. For a stable formatting setup today, run Ruff directly (uvx ruff format) or pin a uv version in CI.
pip compatibility
If you have existing workflows built around pip and requirements files, uv provides a drop-in compatible interface:
# Install from requirements.txt
uv pip install -r requirements.txt
# Install a package
uv pip install requests
# Generate a locked requirements file from loose constraints
uv pip compile requirements.in -o requirements.txt
# Sync an environment to match a requirements file exactly
uv pip sync requirements.txtTip
These commands produce identical results to their pip equivalents but run significantly faster. Swap pip for uv pip in your CI scripts for an immediate speed boost without changing your project structure.
uv pip compile takes a loose requirements.in file (or pyproject.toml) and produces a fully resolved requirements.txt with pinned versions and hashes. The --universal flag generates a single requirements file that works across platforms, a feature pip-tools does not offer.
For a full comparison of pip and uv, see What’s the difference between pip and uv?. To migrate an existing requirements.txt-based project to uv’s native project format, follow How to migrate from requirements.txt to pyproject.toml with uv.
You can also use pip inside a uv-managed virtual environment if needed. See How to use pip in a uv virtual environment.
What’s new in uv
uv ships releases roughly weekly, so the command set keeps growing. A few recent additions are worth knowing about; the uv changelog tracks every release.
uv formatwraps Ruff’s formatter so you can format a project without adding Ruff as an explicit dependency. It is experimental as of 0.11.18 and prints a preview warning. See Formatting code.uv auditscans a project’s locked dependencies for known vulnerabilities and adverse project statuses such as yanked or deprecated releases. It is also experimental. Pair it with How to protect against Python supply chain attacks with uv.uv authmanages credentials for package indexes throughuv auth login,logout, andtoken, replacing hand-edited netrc files for private registries. See How to use private package indexes with uv.uv_buildis uv’s own build backend and the default for packaged projects since July 2025, covered under Advanced topics.uv python upgradeupgrades a uv-managed interpreter to the latest patch release in place. See How to keep Python up to date with uv python upgrade.
For reproducible resolutions that ignore packages published after a cutoff date, the --exclude-newer flag is covered in How to use exclude-newer for reproducible Python environments.
How uv compares to alternatives
| Feature | uv | pip | Poetry | pyenv | pipx |
|---|---|---|---|---|---|
| Install packages | Yes | Yes | Yes | No | No |
| Lockfile | Yes | No | Yes | No | No |
| Virtual environments | Yes | No | Yes | No | Yes (per-tool) |
| Python version management | Yes | No | No | Yes | No |
| Run CLI tools | Yes | No | No | No | Yes |
| Script execution | Yes | No | No | No | No |
| Performance | 15-50x faster | Baseline | Pure Python | Compiles from source | Pure Python |
| Standards compliance | pyproject.toml (PEP 621) | requirements.txt | pyproject.toml (PEP 621 in 2.0+) | N/A | N/A |
For in-depth comparisons:
- What’s the difference between pip and uv?
- How do uv and Poetry compare?
- How do pyenv and uv compare?
When to use uv
New projects: uv is the strongest default for new Python projects today. A single uv init gives you a standards-compliant pyproject.toml, a lockfile, and a virtual environment. No other tool setup required. uv is still on 0.x version numbers, but its core project, packaging, and pip-compatible commands are stable and run in production CI pipelines across the ecosystem; pin a specific uv version in CI for reproducibility and treat the experimental uv format and uv audit commands accordingly.
Existing pip/requirements.txt projects: Start by replacing pip install with uv pip install for an immediate speed boost. When ready, migrate to pyproject.toml with uv init and uv add. The guide How to migrate from requirements.txt to pyproject.toml with uv covers this process.
Existing Poetry projects: uv supports the same pyproject.toml-based workflow with better performance and standards compliance. See How to migrate from Poetry to uv.
CI/CD pipelines: uv’s speed advantage is most dramatic in CI, where environments are recreated on every run. Its caching and fast resolution cut minutes from pipeline execution. See Setting up GitHub Actions with uv and How to use uv in a Dockerfile.
One-off scripts and automation: Inline script dependencies (PEP 723) make uv a strong choice for standalone scripts. You can share a single .py file with its dependencies declared inside, and anyone with uv installed can run it without further setup. This is useful for data processing scripts, automation tasks, and quick prototypes.
Important
If your project depends on non-Python libraries (C/Fortran extensions for scientific computing, CUDA binaries), conda manages those cross-language dependencies in ways uv cannot. See Why should I choose conda? for guidance. For installing GPU-accelerated stacks like RAPIDS with uv directly, see How to install RAPIDS with uv.
Some organizations with established Poetry workflows may prefer to stay with Poetry if the migration cost outweighs the benefits; see How do uv and Poetry compare? for a balanced assessment.
Editor and tool integration
uv creates ordinary virtual environments in .venv, following the same structure as venv and virtualenv. Most editors detect this directory automatically and configure the Python interpreter path. Some need manual configuration, particularly when the .venv directory is in a non-standard location or when using workspace setups.
- How to configure VS Code for a uv project
- How to configure Cursor for a uv project
- How to configure Cursor rules to use uv
- How to configure Claude Code to use uv
- How to create a new Python project with Codex
For project-scaffolding instructions you can paste into a CLAUDE.md or session prompt, see the Modern Python Project Setup Guide for AI Assistants.
For task running, see How to use Poe the Poet as a task runner with uv.
Testing with uv
uv pairs well with pytest for running tests:
uv add --dev pytest
uv run pytestFor testing across multiple Python versions, uv run --python downloads any missing version on demand:
uv run --python 3.11 pytest
uv run --python 3.12 pytest
uv run --python 3.13 pytestGuides:
- How to run tests using uv
- How to fix common pytest errors with uv
- How to test against multiple Python versions using uv
- Setting up testing with pytest and uv (tutorial)
Advanced topics
Virtual environment customization: By default, uv creates a .venv directory in your project root. Some workflows require placing the environment elsewhere, for example when working with Docker volumes or shared filesystems where the default location causes performance issues. See How to customize uv’s virtual environment location.
Docker: uv’s standalone binary and fast installs make it well-suited for container builds. Because uv has no runtime dependency on Python, you can copy the binary into a Docker image and use it immediately. Combined with its caching behavior and fast resolution, this can cut Docker build times from minutes to seconds when dependencies haven’t changed. See How to use uv in a Dockerfile.
Dynamic versioning: For packages that derive their version from git tags or other sources, see How to add dynamic versioning to uv projects.
Build backend: when you create a packaged project (uv init --package or uv init --lib), uv defaults to uv_build, uv’s own build backend (stable since July 2025). Earlier versions defaulted to Hatchling. A plain uv init application has no build backend until you add one. Either way, uv build produces standard wheels and sdists that work with pip and any PEP 517-compatible tool. For background on the original Hatchling default, see Why did uv originally use Hatch as a build backend?.
Publishing packages: Once you’ve built a package, uv can publish it to PyPI:
uv build
uv publishFor a walkthrough of the full publishing process, see Publishing your first Python package to PyPI.
Troubleshooting
Most uv errors have clear messages that point to the fix. Two common issues new users encounter:
Warning
The “No project Table Found” error occurs when you run uv add or uv sync in a directory without a pyproject.toml, or with a pyproject.toml that lacks a [project] section. See How to fix “No project Table Found” error in uv.
Warning
Python version incompatibility errors happen when your pyproject.toml specifies a requires-python range that conflicts with the installed interpreter or with a dependency’s requirements. See How to fix Python version incompatibility errors in uv.
Learn more
Tutorials
- Create your first Python project
- Run your first Python script with uv
- Setting up testing with pytest and uv
- Setting up GitHub Actions with uv
How-to guides
- How to install uv
- How to install Python with uv
- How to run a Jupyter Notebook with uv
- How to migrate from Poetry to uv
- How to migrate from requirements.txt to pyproject.toml with uv
Explanations
- Why you should try uv if you use Python
- What’s the difference between pip and uv?
- How do uv and Poetry compare?
- Understanding uv init project types
- When to use uv run vs uvx
- Why are there so many Python packaging tools?