Skip to content
Go back

UV for Python - Fast Package Management

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):

The speed comes from:

Unified Tool

UV replaces multiple tools:

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:

# Clear cache
uv cache clean

# Show cache size
uv cache dir

Performance Metrics

Real project installation times (30 packages, cold cache):

ToolTimeCache Hit Time
pip42s28s
poetry38s25s
UV3s0.8s

CI/CD pipeline comparison (FastAPI project):

StagepipUVImprovement
Install35s2s17.5x
LockN/A1sN/A
Total35s3s11.7x

Limitations

Current Constraints

UV does not support:

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

FeaturepippoetryUV
Install speedBaseline1.1x10-100x
Dependency resolutionBacktrackingSAT solverParallel resolver
Lock fileManualpoetry.lockuv.lock
Python version managementNoNoYes
Virtual environmentsExternal toolBuilt-inBuilt-in
Private indexesYesYesYes
PEP 621 supportPartialNoYes

Resources:


Share this post on:

Previous Post
Using AWS Pandas Layer (AWS Wrangler) in Serverless Framework
Next Post
Mastering Go's net/http Package - Building Production-Ready HTTP Services