uv: The Python Package Manager That Replaces pip, pyenv, and Poetry (2026)
The Python packaging ecosystem has long been fragmented. Installing a project typically meant juggling five or more tools: pyenv to manage Python versions, virtualenv to isolate environments, pip to install packages, pip-tools to compile lock files, and pipx to install CLI tools globally. Poetry or PDM for project management on top of that. Each tool has its own configuration, its own quirks, and its own learning curve.
uv collapses all of that into a single binary written in Rust. It was created by Astral — the same team behind ruff, the fastest Python linter — and it is genuinely 10 to 100 times faster than pip. Benchmarks show installing 23 packages takes pip 6.6 seconds and uv 0.15 seconds on a warm cache. On cold cache the difference is similar in scale.
This tutorial covers everything you need to switch your workflow to uv today.
What uv Replaces
Before uv, a typical Python developer's toolbox looked like this:
pip — install packages
pyenv — manage Python versions
virtualenv — create isolated environments
pip-tools — compile requirements.txt lock files
pipx — install CLI tools in isolated environments
poetry/PDM — project management and publishing
twine — upload packages to PyPI
With uv, all of those collapse into one binary:
uv — everything above, in one command
uv is not a wrapper around pip. It is a fully independent implementation of the Python package resolver and installer written in Rust. It speaks the same protocols (PyPI, PEP 517, PEP 660) so it is fully compatible with the existing ecosystem.
Installation
uv does not require Python to be installed first. Download a single static binary:
# Linux and macOS
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# Homebrew
brew install uv
# pip (if you already have Python)
pip install uv
After installation, restart your shell or source your profile. Verify:
uv --version
# uv 0.4.x
That is the entire installation. No dependencies, no Python required, no package managers to bootstrap.
Project Setup
Starting a New Project
uv init myproject
cd myproject
This creates a standard Python project layout:
myproject/
├── pyproject.toml
├── .python-version
├── README.md
└── src/
└── myproject/
└── __init__.py
Adding Dependencies
uv add requests fastapi
uv add --dev pytest ruff
uv resolves, downloads, and installs packages, then updates pyproject.toml and generates a uv.lock file automatically. No separate pip install step.
Running Code
uv run main.py
uv run python -c "import requests; print(requests.__version__)"
uv run pytest
uv run automatically activates the virtual environment for the duration of the command. You never need to run source .venv/bin/activate manually.
Python Version Management
uv manages Python installations directly, replacing pyenv entirely.
Install Python Versions
uv python install 3.12
uv python install 3.11 3.10 # install multiple at once
uv python install # install the version in .python-version
Pin a Version for a Project
uv python pin 3.11
# writes 3.11 to .python-version
List Available and Installed Versions
uv python list
# cpython-3.13.0-linux-x86_64-gnu <download available>
# cpython-3.12.7-linux-x86_64-gnu /home/user/.local/share/uv/python/...
# cpython-3.11.10-linux-x86_64-gnu /home/user/.local/share/uv/python/...
uv downloads official CPython builds from the Astral-maintained python-build-standalone project. These are statically linked, relocatable binaries that work on any compatible Linux distribution.
Virtual Environments
Creating Environments
# Create a venv in the current directory
uv venv
# Create with a specific Python version
uv venv --python 3.11
# Create at a custom path
uv venv /path/to/myenv
No Manual Activation Needed
The key change in workflow is that uv run handles activation automatically:
# Old workflow
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python main.py
deactivate
# New workflow
uv run main.py
If you do need to activate (for interactive shells, for example):
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
Lock Files and Reproducible Installs
uv.lock
When you add a dependency, uv creates uv.lock. This is a cross-platform lock file that pins every package, including transitive dependencies, to exact versions and content hashes.
uv add django
# Resolves dependencies...
# Writes uv.lock
The lock file should be committed to version control. It ensures every developer and every CI run installs the exact same dependency tree.
Syncing from the Lock File
uv sync # install all dependencies from uv.lock
uv sync --frozen # fail if uv.lock would change (good for CI)
Updating Dependencies
uv lock --upgrade # upgrade all packages to latest allowed versions
uv lock --upgrade-package requests # upgrade only one package
pip Compatibility Mode
If you have an existing project using requirements.txt, uv provides a fully pip-compatible interface:
# Install from requirements.txt
uv pip install -r requirements.txt
# Install a package
uv pip install django==5.0
# Compile requirements.in → requirements.txt (replaces pip-tools)
uv pip compile requirements.in -o requirements.txt
# Show installed packages
uv pip list
# Freeze current environment
uv pip freeze > requirements.txt
This means you can drop uv into any existing project and immediately benefit from the speed improvement without changing any other tooling.
Migrating from pip or Poetry in 5 Minutes
From a pip / requirements.txt Project
# Step 1: Create a uv project from your existing directory
uv init --no-workspace
# Step 2: Import your requirements
uv add $(cat requirements.txt | grep -v "^#" | tr '\n' ' ')
# Or keep using requirements.txt directly:
uv pip install -r requirements.txt
# Step 3: Generate a lock file
uv lock
# Step 4: Replace your workflow
# Before: pip install -r requirements.txt && python main.py
# After: uv run main.py
From a Poetry Project
# Step 1: uv can read pyproject.toml directly
# Just remove the [tool.poetry] section and replace with standard [project]
# Step 2: Import Poetry dependencies to uv
uv add $(poetry show --no-dev --no-ansi | awk '{print $1}')
# Step 3: Add dev dependencies
uv add --dev $(poetry show --dev --no-ansi | awk '{print $1}')
# Step 4: Delete poetry.lock, add uv.lock to version control
rm poetry.lock
uv lock
git add uv.lock
The whole migration for a medium-sized project typically takes under five minutes.
PEP 723 Inline Script Dependencies
PEP 723 allows embedding dependency metadata directly in a Python script. uv supports this natively:
#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests",
# "rich",
# ]
# ///
import requests
from rich import print
response = requests.get("https://api.github.com/repos/astral-sh/uv")
print(response.json()["description"])
Run it directly:
uv run script.py
uv reads the # /// script block, creates an isolated temporary environment, installs the listed dependencies, and executes the script. No pip install, no virtualenv, no setup. This makes Python scripts fully self-contained and shareable.
Global Tool Installation (Replacing pipx)
uv tool replaces pipx for installing CLI tools in isolated environments:
# Install a tool globally
uv tool install ruff
uv tool install httpie
uv tool install black
# Run a tool without installing it
uv tool run ruff check .
# or the short form:
uvx ruff check .
# List installed tools
uv tool list
# Upgrade a tool
uv tool upgrade ruff
# Uninstall
uv tool uninstall black
Tools installed with uv tool get their own isolated environment but are available on your PATH. uvx is a shortcut for uv tool run — great for one-off commands without a permanent install.
Publishing Packages (Replacing twine)
# Build the package
uv build
# Creates dist/mypackage-0.1.0.tar.gz and dist/mypackage-0.1.0-py3-none-any.whl
# Publish to PyPI
uv publish
# Publish to a custom index
uv publish --index https://my-private-pypi.example.com/
# Publish with explicit credentials
uv publish --token $PYPI_TOKEN
uv publish replaces both python -m build and twine upload in a single command.
CI/CD with uv
GitHub Actions
Here is a complete workflow showing how to use uv in CI. The speed improvement on install steps is typically 60–80% compared to pip.
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
enable-cache: true
- name: Set up Python
run: uv python install
- name: Install dependencies
run: uv sync --frozen
- name: Run tests
run: uv run pytest
- name: Lint
run: uv run ruff check .
The astral-sh/setup-uv action caches the uv binary and the package cache across runs, making subsequent CI runs even faster. The --frozen flag on uv sync ensures the CI fails if anyone forgot to update the lock file.
GitLab CI
image: python:3.12-slim
variables:
UV_CACHE_DIR: "$CI_PROJECT_DIR/.uv-cache"
cache:
paths:
- .uv-cache/
before_script:
- pip install uv
- uv sync --frozen
test:
script:
- uv run pytest
lint:
script:
- uv run ruff check .
Workspace Support for Monorepos
If your repository contains multiple Python packages, uv supports workspaces:
# Root pyproject.toml
[tool.uv.workspace]
members = ["packages/*"]
mymonorepo/
├── pyproject.toml # workspace root
├── uv.lock # single lock file for all packages
└── packages/
├── api/
│ └── pyproject.toml
├── core/
│ └── pyproject.toml
└── cli/
└── pyproject.toml
All packages in the workspace share a single lock file and can declare dependencies on each other:
# packages/api/pyproject.toml
[project]
name = "api"
dependencies = [
"core", # local workspace package
"fastapi",
]
uv sync # sync all packages
uv run --package api main.py # run in a specific package context
Key Commands Reference
| Task | uv command |
|---|---|
| New project | uv init myproject |
| Add dependency | uv add requests |
| Add dev dependency | uv add --dev pytest |
| Remove dependency | uv remove requests |
| Install all deps | uv sync |
| Run script | uv run main.py |
| Install Python | uv python install 3.12 |
| Pin Python version | uv python pin 3.11 |
| Install global tool | uv tool install ruff |
| Run one-off tool | uvx ruff check . |
| Build package | uv build |
| Publish package | uv publish |
| pip compatibility | uv pip install -r requirements.txt |
Summary
uv is the most significant shift in Python tooling since virtualenv was introduced. It is faster than any alternative by a large margin, it consolidates a fragmented toolchain into a single binary, and it is backward-compatible with the existing pip and PyPI ecosystem.
For new projects, start with uv init and never look back. For existing projects, the migration path is straightforward: add uv.lock to version control, replace pip install with uv sync, and replace python with uv run in your scripts and CI. The performance improvement on CI alone typically justifies the five-minute migration cost.
Install it, try uv run on your next script, and see for yourself.
curl -LsSf https://astral.sh/uv/install.sh | sh