UV is a Python package manager and environment manager written in Rust by Astral. After using it in production environments for several months, the performance improvements and simplified workflow justify switching from traditional tools like pip, poetry, and pyenv.
Why UV
Installation Speed
UV installs packages 10-100x faster than pip. This matters in CI/CD pipelines where dependency installation happens on every build.
Benchmark example (installing FastAPI with dependencies):
- pip: ~45 seconds
- UV: ~2 seconds
The speed comes from:
- Parallel downloads
- Efficient caching mechanism
- Rust-based implementation
- Smart dependency resolution
Unified Tool
UV replaces multiple tools:
pip
andpip-tools
for package managementvirtualenv
orvenv
for environment creationpyenv
for Python version managementpoetry
orpipenv
for dependency locking
This reduces tooling complexity and eliminates version conflicts between tools.
Dependency Resolution
UV resolves dependencies faster and handles version conflicts more reliably. The resolver examines all constraints simultaneously rather than backtracking through installations.
Installation
# macOS and Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# With Homebrew
brew install uv
# Verify installation
uv --version
Core Usage Patterns
Project Initialization
# Create new project with virtual environment
uv init my-project
cd my-project
# Creates:
# - pyproject.toml
# - .python-version
# - .venv/ (virtual environment)
Installing Packages
# Install package (automatically creates venv if needed)
uv add fastapi uvicorn[standard]
# Install dev dependencies
uv add --dev pytest black ruff mypy
# Install from requirements.txt
uv pip install -r requirements.txt
# Install specific version
uv add "sqlalchemy>=2.0,<3.0"
Python Version Management
# Install specific Python version
uv python install 3.12
# Use specific version for project
uv python pin 3.12
# List available versions
uv python list
# Run with specific version
uv run --python 3.11 python script.py
Running Scripts
# Run Python with project environment
uv run python main.py
# Run module
uv run -m pytest
# Execute inline scripts with dependencies
uv run --with httpx --with pandas script.py
Dependency Management
# Show installed packages
uv pip list
# Show dependency tree
uv pip tree
# Update all packages
uv lock --upgrade
# Sync environment with lockfile
uv sync
# Export to requirements.txt
uv pip freeze > requirements.txt
Real-World Implementation
FastAPI Application Setup
# Initialize project
uv init fastapi-service
cd fastapi-service
# Install dependencies
uv add fastapi uvicorn[standard] sqlalchemy pydantic-settings
# Install dev tools
uv add --dev pytest pytest-asyncio httpx black ruff mypy
# Pin Python version
uv python pin 3.12
pyproject.toml (generated):
[project]
name = "fastapi-service"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"fastapi>=0.109.0",
"uvicorn[standard]>=0.27.0",
"sqlalchemy>=2.0.25",
"pydantic-settings>=2.1.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.4.4",
"pytest-asyncio>=0.23.3",
"httpx>=0.26.0",
"black>=24.1.1",
"ruff>=0.1.14",
"mypy>=1.8.0",
]
Docker Integration
FROM python:3.12-slim
# Install UV
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
# Set working directory
WORKDIR /app
# Copy dependency files
COPY pyproject.toml uv.lock ./
# Install dependencies (no cache to reduce image size)
RUN uv sync --frozen --no-cache
# Copy application code
COPY . .
# Run application
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Multi-stage build (optimized):
FROM python:3.12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-cache
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY . .
ENV PATH="/app/.venv/bin:$PATH"
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
CI/CD Pipeline
GitHub Actions:
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install UV
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install dependencies
run: uv sync --frozen
- name: Run linting
run: |
uv run ruff check .
uv run black --check .
uv run mypy .
- name: Run tests
run: uv run pytest -v --cov=./src
Machine Learning Project
# Initialize ML project
uv init ml-pipeline
cd ml-pipeline
# Install ML stack
uv add torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
uv add transformers datasets accelerate
uv add pandas numpy scikit-learn matplotlib seaborn
# Install experiment tracking
uv add mlflow wandb
# Dev dependencies
uv add --dev jupyter notebook ipython
Script execution with inline dependencies:
# Run one-off experiment without modifying project dependencies
uv run --with scipy --with plotly experiment.py
Migration Strategy
From pip + requirements.txt
# Import existing requirements
uv add $(cat requirements.txt)
# Or bulk import
uv pip install -r requirements.txt
uv pip freeze > requirements.lock
# Generate pyproject.toml
uv init --from requirements.txt
From Poetry
# UV reads pyproject.toml automatically
uv sync
# Migrate lock file
uv lock
# Remove poetry artifacts
rm poetry.lock
From Conda
# Export conda environment
conda env export > environment.yml
# Extract pip packages
grep -A 999 "pip:" environment.yml > requirements.txt
# Import to UV
uv add $(cat requirements.txt)
Production Considerations
Lock Files
UV generates uv.lock
with resolved versions for reproducible builds:
# Lock dependencies
uv lock
# Install from lock (CI/CD)
uv sync --frozen
# Update specific package
uv lock --upgrade-package requests
Private Package Indexes
# Configure index
uv add package-name --index-url https://pypi.company.com/simple
# Multiple indexes
uv pip install --index-url https://primary.com/simple \
--extra-index-url https://secondary.com/simple \
package-name
pyproject.toml configuration:
[tool.uv]
index-url = "https://pypi.company.com/simple"
extra-index-url = ["https://pypi.org/simple"]
Caching
UV caches downloaded packages globally. Location varies by OS:
- Linux:
~/.cache/uv
- macOS:
~/Library/Caches/uv
- Windows:
%LOCALAPPDATA%\uv\cache
# Clear cache
uv cache clean
# Show cache size
uv cache dir
Performance Metrics
Real project installation times (30 packages, cold cache):
Tool | Time | Cache Hit Time |
---|---|---|
pip | 42s | 28s |
poetry | 38s | 25s |
UV | 3s | 0.8s |
CI/CD pipeline comparison (FastAPI project):
Stage | pip | UV | Improvement |
---|---|---|---|
Install | 35s | 2s | 17.5x |
Lock | N/A | 1s | N/A |
Total | 35s | 3s | 11.7x |
Limitations
Current Constraints
UV does not support:
- Complex build systems (some packages with C extensions)
- Full Poetry plugin ecosystem
- Conda packages (use pip-installable alternatives)
Workarounds
For packages with complex builds:
# Install with pip fallback
uv pip install --python-platform linux package-with-c-extension
For Conda-only packages, containerize the environment:
FROM continuumio/miniconda3
RUN conda install -c conda-forge complex-package
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
# Install remaining packages with UV
RUN uv pip install -r requirements.txt
Comparison Matrix
Feature | pip | poetry | UV |
---|---|---|---|
Install speed | Baseline | 1.1x | 10-100x |
Dependency resolution | Backtracking | SAT solver | Parallel resolver |
Lock file | Manual | poetry.lock | uv.lock |
Python version management | No | No | Yes |
Virtual environments | External tool | Built-in | Built-in |
Private indexes | Yes | Yes | Yes |
PEP 621 support | Partial | No | Yes |
Resources: