uv: A Complete Guide to Python's Fastest Package Manager
uv is a Python package and project manager that replaces a half-dozen traditional tools with a single binary. Where Python developers once needed separate programs to install interpreters, create virtual environments, manage dependencies, lock versions, run CLI tools, and format code, uv handles all of it with 10-100x faster performance than the tools it replaces.
Why uv exists
Python’s packaging ecosystem evolved tool-by-tool over two decades. pip installs packages but doesn’t lock dependency versions (though an experimental pip lock command following PEP 751 is in progress). virtualenv isolates environments but doesn’t manage interpreters. pyenv manages interpreters but doesn’t touch packages. pipx installs CLI tools but only in isolated environments. pip-tools locks dependencies but requires pip underneath. Poetry unifies some of these, but historically used its own non-standard metadata format (Poetry 2.0, released January 2025, added PEP 621 support) and has no built-in Python version management.
Each tool has its own installation process, its own configuration format, and its own mental model. A developer setting up a new project might install pyenv to get the right Python version, create a virtualenv, configure pip-tools for locking, and install pipx for command-line utilities. That’s four tools before writing a line of application code.
uv collapses this stack. Built by Astral (the team behind Ruff), it provides a single command-line interface for Python version management, virtual environment creation, dependency resolution, lockfile generation, script execution, tool management, and code formatting. Its speed advantage comes from aggressive caching, parallel downloads, and an optimized resolver.
uv also 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.
Note
Projects created with uv remain compatible with other Python tools. You aren’t locked in.
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.
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 -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
When you run uv run or uv sync inside a project, uv automatically downloads 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. A uv python install call typically completes 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
The uv.lock file records the exact resolved versions of every direct and transitive dependency, 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. This follows the same patterns used by pip and other tools, so the concepts transfer.
For a hands-on walkthrough of creating a project from scratch, follow the tutorial Create your first Python project.
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 deserve their own mention. The uv.lock file is central to reproducible environments. It locks 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 lock file? 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.
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 built-in 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.
# 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.
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.
The uv pip compile command deserves special attention. It 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, which is 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.
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 | 10-100x 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:
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.
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.
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
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 python install 3.11 3.12 3.13
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: uv init now defaults to uv_build, uv’s own build backend (stable since July 2025). Earlier versions defaulted to Hatchling. 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?
Reference
Also Mentioned In
Get Python tooling updates
Subscribe to the newsletter