Python uv: The Fast Package Manager Guide

Published February 12, 2026 · 22 min read

uv is an ultra-fast Python package and project manager written in Rust, built by Astral (the team behind ruff). It is a single tool that replaces pip, pip-tools, pipx, poetry, pyenv, and virtualenv. Dependency resolution that takes pip 30 seconds often completes in under 1 second with uv.

What you'll learn: Installation, project initialization, dependency management, lockfiles, virtual environments, Python version management, running scripts, pip compatibility mode, CLI tool installation, workspaces for monorepos, CI/CD integration, Docker builds, and migration from pip, poetry, and pipenv.

1. Why uv?

The Python packaging ecosystem has long been a patchwork of tools: pip for installing, venv for environments, pip-tools for lockfiles, pyenv for Python versions, pipx for CLI tools. Each has its own configuration, quirks, and failure modes. uv unifies all of these into a single, fast binary.

2. Installation

Standalone installer (recommended)

# Linux / macOS / WSL
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.sh | iex"

# Verify
uv --version
# uv 0.6.x

The installer places the binary in ~/.local/bin (Linux/macOS) or %USERPROFILE%\.local\bin (Windows). It adds this to your PATH automatically.

Other installation methods

# Via pip / Homebrew / conda
pip install uv
brew install uv
conda install -c conda-forge uv

# Pin a specific version
curl -LsSf https://astral.sh/uv/0.6.0/install.sh | sh

# Self-update
uv self update

Shell completions

uv generate-shell-completion bash > ~/.local/share/bash-completion/completions/uv
uv generate-shell-completion zsh > ~/.zfunc/_uv
uv generate-shell-completion fish > ~/.config/fish/completions/uv.fish

3. Project Setup with uv init

Creating a new project

# Create a new application project
uv init my-app
cd my-app

# Creates:
# my-app/
#   .python-version      # e.g., 3.12
#   pyproject.toml        # PEP 621 project metadata
#   README.md
#   hello.py              # sample entry point

# Create a library project (for publishing to PyPI)
uv init --lib my-library
# Creates src/my_library/__init__.py with proper package structure

# Create a packaged application (with src layout)
uv init --package my-service

Initializing in an existing directory

# In an existing project directory
cd existing-project
uv init

# uv detects existing pyproject.toml and works with it
# If you have a Poetry/PDM project, uv reads their formats too

The generated pyproject.toml

[project]
name = "my-app"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

This is standard PEP 621 format — no [tool.poetry] or proprietary sections. Any PEP-compliant tool can read it.

4. Dependency Management

Adding dependencies

# Add a runtime dependency
uv add requests

# Add with version constraint
uv add "django>=4.2,<5.0"

# Add multiple at once
uv add flask sqlalchemy alembic

# Add with extras
uv add "fastapi[standard]"

# Add from git
uv add git+https://github.com/encode/httpx

# Add from a git tag or branch
uv add git+https://github.com/encode/httpx@0.27.0
uv add git+https://github.com/encode/httpx@main

# Add a local path dependency (editable)
uv add --editable ../shared-lib

# Add from requirements.txt
uv add -r requirements.txt

Each uv add updates pyproject.toml, resolves the full dependency tree, writes uv.lock, and syncs the virtual environment — all in one step.

Adding dev dependencies

# Add to the dev dependency group
uv add --dev pytest ruff mypy

# Add to a custom group
uv add --group docs sphinx sphinx-rtd-theme
uv add --group test pytest-cov factory-boy

Dev and group dependencies appear in pyproject.toml under [dependency-groups]:

[dependency-groups]
dev = ["pytest>=8.0", "ruff>=0.9", "mypy>=1.14"]
docs = ["sphinx>=7.2", "sphinx-rtd-theme>=2.0"]
test = ["pytest-cov>=6.0", "factory-boy>=3.3"]

Removing dependencies

uv remove requests
uv remove --dev pytest
uv remove --group docs sphinx

Updating dependencies

# Update a single package to its latest allowed version
uv lock --upgrade-package requests
uv sync

# Update all packages
uv lock --upgrade
uv sync

# See what's currently installed
uv pip list

5. The Lock File: uv.lock

uv produces a cross-platform lock file that pins every dependency (including transitive ones) with exact versions and hashes. Unlike poetry.lock, uv.lock is a human-readable TOML file that locks for all platforms simultaneously.

# Generate or update the lock file (without installing)
uv lock

# Install exactly what's in the lock file
uv sync

# Install without dev dependencies (production)
uv sync --no-dev

# Install only specific groups
uv sync --group docs

# Verify lock file is up-to-date with pyproject.toml
uv lock --check

Always commit uv.lock to version control. This ensures every developer and CI server installs the exact same dependency versions.

6. Running Code with uv run

uv run executes a command in the project's virtual environment. It automatically creates the environment and installs dependencies if needed.

# Run a Python script
uv run python app.py

# Run pytest
uv run pytest

# Run any installed CLI tool
uv run ruff check .
uv run mypy src/

# Pass arguments
uv run python -m flask run --port 8080

# Run with an inline dependency (no pyproject.toml needed)
uv run --with requests python -c "import requests; print(requests.get('https://httpbin.org/ip').json())"

Inline script dependencies

uv supports PEP 723 inline script metadata. Add dependency declarations directly in a Python file:

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "requests>=2.31",
#     "rich>=13.0",
# ]
# ///

import requests
from rich import print

resp = requests.get("https://api.github.com/zen")
print(f"[bold green]{resp.text}[/bold green]")
# uv reads the metadata and installs deps automatically
uv run fetch_zen.py

This is incredibly powerful for standalone scripts — no pyproject.toml, no requirements.txt, no virtual environment setup. Just run it.

7. Managing Python Versions

uv can download and manage Python installations, replacing pyenv entirely.

# Install a specific Python version
uv python install 3.12
uv python install 3.11 3.13

# Install the latest available
uv python install

# List installed versions
uv python list

# Pin a version for the current project
uv python pin 3.12

# This creates a .python-version file
# uv reads it automatically for uv run, uv sync, etc.

# Find where a specific version is installed
uv python find 3.12

# Uninstall a version
uv python uninstall 3.11

When you run uv sync or uv run, uv automatically downloads the required Python version if it is not already available. The requires-python field in pyproject.toml determines compatible versions.

8. Virtual Environments

uv creates and manages virtual environments automatically when you use project commands (uv sync, uv run). You can also manage them manually.

# Create a virtual environment (default: .venv in current dir)
uv venv

# Create with a specific Python version
uv venv --python 3.12

# Create in a custom location
uv venv /path/to/my-env

# Create with a specific name
uv venv .venv-test

# Activate it (standard activation, same as any venv)
source .venv/bin/activate    # Linux/macOS
.venv\Scripts\activate       # Windows

In practice, you rarely need to create or activate environments manually. uv run handles it automatically. The environment lives at .venv/ in your project root.

9. Pip Compatibility Mode

uv provides a full pip-compatible interface for existing workflows and scripts that expect pip commands.

# Direct pip replacements — same flags, 10-100x faster
uv pip install requests
uv pip install -r requirements.txt
uv pip install -e ".[dev]"
uv pip uninstall requests
uv pip freeze > requirements.txt
uv pip list
uv pip show requests

# Compile a requirements.txt lock file (replaces pip-tools)
uv pip compile requirements.in -o requirements.txt
uv pip compile pyproject.toml -o requirements.txt

# Sync environment to match requirements exactly
uv pip sync requirements.txt

uv pip compile replaces pip-compile from pip-tools. It takes loose requirements and produces a fully pinned requirements.txt with hashes.

10. Installing CLI Tools with uv tool

uv tool replaces pipx — it installs Python CLI tools in isolated environments so they do not pollute your system packages.

# Install a CLI tool globally
uv tool install ruff
uv tool install black
uv tool install httpie

# Run a tool without installing it (like npx)
uvx ruff check .
uvx black --check .
uvx cowsay "hello from uv"

# Run a specific version
uvx ruff@0.9.0 check .

# List installed tools
uv tool list

# Upgrade a tool
uv tool upgrade ruff
uv tool upgrade --all

# Uninstall
uv tool uninstall ruff

uvx is shorthand for uv tool run. It downloads the tool into a temporary, cached environment and runs it immediately. Perfect for one-off tasks.

11. pyproject.toml Configuration

A full pyproject.toml for a uv-managed project:

[project]
name = "my-service"
version = "1.2.0"
description = "A web service built with FastAPI"
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.11"
authors = [{ name = "Jane Doe", email = "jane@example.com" }]
keywords = ["api", "web", "fastapi"]
dependencies = [
    "fastapi[standard]>=0.115",
    "sqlalchemy>=2.0",
    "alembic>=1.14",
    "pydantic-settings>=2.0",
]

[project.scripts]
my-service = "my_service.cli:main"

[project.urls]
Repository = "https://github.com/jane/my-service"

[dependency-groups]
dev = ["pytest>=8.0", "ruff>=0.9", "mypy>=1.14"]
docs = ["sphinx>=7.2", "sphinx-rtd-theme>=2.0"]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.ruff]
line-length = 100
target-version = "py311"

[tool.pytest.ini_options]
testpaths = ["tests"]

The [tool.uv] section supports additional configuration like custom indexes (index = [...]), dependency overrides (override-dependencies), and Python version constraints (python-version = "3.12").

12. Workspaces for Monorepos

uv workspaces let you manage multiple packages in a single repository with a shared lock file and virtual environment.

# Project structure
monorepo/
  pyproject.toml          # root workspace config
  uv.lock                 # shared lock file
  packages/
    core/
      pyproject.toml
      src/core/...
    api/
      pyproject.toml
      src/api/...
    worker/
      pyproject.toml
      src/worker/...

Root pyproject.toml

[project]
name = "monorepo"
version = "0.0.0"
requires-python = ">=3.12"

[tool.uv.workspace]
members = ["packages/*"]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

Member pyproject.toml (packages/api)

[project]
name = "api"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "core",           # references the workspace member
    "fastapi>=0.115",
]

[tool.uv.sources]
core = { workspace = true }

Working with workspaces

# Sync the entire workspace
uv sync

# Run a command in a specific package
uv run --package api python -m api.main

# Add a dependency to a specific package
uv add --package worker celery

# Lock the entire workspace
uv lock

13. Migration Guides

From pip + requirements.txt

# Step 1: Initialize a uv project
uv init

# Step 2: Add your existing dependencies
uv add -r requirements.txt

# Step 3: Add dev dependencies
uv add --dev -r requirements-dev.txt

# Step 4: Verify everything installs
uv sync

# Step 5: Run your tests
uv run pytest

# You can now delete requirements.txt and use uv.lock instead
# Or keep generating it: uv pip compile pyproject.toml -o requirements.txt

From Poetry

# uv reads pyproject.toml with [tool.poetry] sections
# Step 1: Generate a uv lock file
uv lock

# Step 2: Sync the environment
uv sync

# Step 3: Verify your project works
uv run pytest

# Step 4: Migrate pyproject.toml to PEP 621 format
# Move [tool.poetry.dependencies] to [project.dependencies]
# Move [tool.poetry.group.dev.dependencies] to [dependency-groups]
# Update [build-system] from poetry-core to hatchling

# Step 5: Remove Poetry artifacts
# Delete poetry.lock (replaced by uv.lock)
# Remove [tool.poetry] sections from pyproject.toml

From pipenv

# Step 1: Export to requirements.txt
pipenv requirements > requirements.txt
pipenv requirements --dev > requirements-dev.txt

# Step 2: Initialize uv project
uv init

# Step 3: Import dependencies
uv add -r requirements.txt
uv add --dev -r requirements-dev.txt

# Step 4: Verify and clean up
uv sync
uv run pytest
# Delete Pipfile, Pipfile.lock

14. CI/CD Integration

GitHub Actions

# .github/workflows/test.yml
name: Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12", "3.13"]
    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true

      - name: Set up Python ${{ matrix.python-version }}
        run: uv python install ${{ matrix.python-version }}

      - name: Install dependencies
        run: uv sync --frozen

      - name: Run tests
        run: uv run pytest --cov=src/

      - name: Lint
        run: uv run ruff check .

--frozen tells uv to use the existing uv.lock without modifying it. This catches cases where someone forgot to update the lock file.

GitLab CI

# .gitlab-ci.yml
image: python:3.12-slim

variables:
  UV_CACHE_DIR: "$CI_PROJECT_DIR/.uv-cache"

cache:
  paths:
    - .uv-cache/
    - .venv/

before_script:
  - pip install uv
  - uv sync --frozen

test:
  script:
    - uv run pytest

lint:
  script:
    - uv run ruff check .

15. Docker Integration

Multi-stage build (recommended)

FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder

WORKDIR /app

# Enable bytecode compilation for faster startup
ENV UV_COMPILE_BYTECODE=1
# Use the system Python (no venv in container)
ENV UV_LINK_MODE=copy

# Install dependencies first (layer caching)
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-install-project --no-dev

# Copy source and install the project itself
COPY . .
RUN uv sync --frozen --no-dev

# --- Runtime stage ---
FROM python:3.12-slim-bookworm

WORKDIR /app
COPY --from=builder /app/.venv .venv
COPY --from=builder /app/src src

ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "my_service"]

Simple single-stage build

FROM python:3.12-slim

# Copy uv binary from official image
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-install-project --no-dev

COPY . .
RUN uv sync --frozen --no-dev

ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "my_service"]

16. Performance Benchmarks

Real-world install times for a typical web project (~40 dependencies):

Scenariopippoetryuv
Cold install (no cache)32s45s2.1s
Warm install (cached)8s12s0.4s
Resolve only (lock)N/A18s0.8s
Add single package5s15s0.5s
Create venv3s4s0.1s

uv achieves this speed through several techniques: parallel downloads and extraction, a global content-addressable cache, hard-linking packages from the cache into virtual environments, and efficient Rust-native dependency resolution.

17. uv vs pip vs Poetry vs PDM

Featurepip + venvPoetryPDMuv
SpeedSlowModerateModerateVery fast
Lock fileManual (freeze)poetry.lockpdm.lockuv.lock
Dependency resolverBasicSAT solverGoodFast SAT solver
Python managementNoNoNoBuilt-in
Venv managementManualAutomaticAutomaticAutomatic
CLI tool installNoNoNouv tool (pipx)
Build & publishNeeds twineBuilt-inBuilt-inuv build / publish
PEP 621N/ASince v2.0YesYes
WorkspacesNoPath depsNoNative
Inline scriptsNoNoNoPEP 723
Cross-platform lockNoNoYesYes
Written inPythonPythonPythonRust

Bottom line: For new projects in 2026, uv is the default recommendation. It is faster, handles more tasks, and follows standards. Use Poetry if your team already has a large Poetry codebase. Use pip for simple, one-off scripts.

18. Tips and Best Practices

Project structure and .gitignore

# Recommended layout
my-app/
  pyproject.toml
  uv.lock              # Always commit this
  .python-version
  src/my_app/
    __init__.py
    main.py
  tests/test_main.py

# .gitignore entries
.venv/
__pycache__/
*.py[cod]

Useful environment variables

UV_COMPILE_BYTECODE=1 uv sync    # Compile bytecode (faster startup)
UV_NO_PROGRESS=1 uv sync         # Quiet mode for CI
UV_CACHE_DIR=/tmp/uv-cache       # Custom cache location
UV_INDEX_URL=https://pypi.mycompany.com/simple/  # Private index

Building and publishing packages

# Build sdist and wheel
uv build

# Publish to PyPI (set UV_PUBLISH_TOKEN env var)
uv publish

# Publish to TestPyPI
uv publish --index-url https://test.pypi.org/legacy/

Quick Reference Cheat Sheet

TaskCommand
Install uvcurl -LsSf https://astral.sh/uv/install.sh | sh
Create new projectuv init my-app
Create library projectuv init --lib my-lib
Add dependencyuv add requests
Add dev dependencyuv add --dev pytest
Remove dependencyuv remove requests
Sync from lock fileuv sync
Production syncuv sync --no-dev
Update lock fileuv lock --upgrade
Run a commanduv run pytest
Run inline scriptuv run script.py
Install Pythonuv python install 3.12
Pin Python versionuv python pin 3.12
Create venvuv venv
Install CLI tooluv tool install ruff
Run tool onceuvx ruff check .
pip compatibilityuv pip install -r requirements.txt
Compile requirementsuv pip compile requirements.in
Build packageuv build
Publish to PyPIuv publish
Self-updateuv self update

Frequently Asked Questions

What is uv and why is it faster than pip?

uv is a Python package manager written in Rust by Astral (the ruff team). It is 10-100x faster than pip because it resolves dependencies in parallel, uses a global cache to avoid redundant downloads, and leverages Rust's performance for all I/O and computation. It replaces pip, pip-tools, pipx, poetry, pyenv, and virtualenv in a single binary.

How do I migrate from pip to uv?

For existing projects with requirements.txt, run uv pip install -r requirements.txt as a drop-in replacement. For a full migration to uv's project system, run uv init, then uv add -r requirements.txt. uv will create a pyproject.toml and uv.lock.

How do I migrate from Poetry to uv?

uv can read Poetry's pyproject.toml format. Run uv lock in a Poetry project to generate a uv.lock file, then uv sync to install everything. Gradually migrate the [tool.poetry] sections to standard PEP 621 [project] format.

Does uv replace pyenv for managing Python versions?

Yes. uv python install 3.12 downloads and manages Python installations directly. It automatically discovers or downloads the right Python version based on your pyproject.toml requires-python field. You no longer need pyenv, deadsnakes PPA, or manual Python builds.

What is the difference between uv sync and uv pip install?

uv sync is the project-aware command: it reads pyproject.toml and uv.lock, creates a virtual environment, and installs exactly the locked dependencies. uv pip install is the pip-compatible interface for ad-hoc installs without a project structure. For repeatable builds, always use uv sync.

Can I use uv in Docker and CI/CD?

Yes. Astral provides official Docker images (ghcr.io/astral-sh/uv) and a GitHub Action (astral-sh/setup-uv). In Docker, copy the uv binary from the official image, then run uv sync --frozen for reproducible installs. In GitHub Actions, use the setup-uv action with cache enabled for fast CI builds.

Does uv support workspaces for monorepos?

Yes. Define workspace members in your root pyproject.toml under [tool.uv.workspace] with a members glob pattern. Each member has its own pyproject.toml but shares a single uv.lock and virtual environment. This is ideal for monorepos with shared libraries and multiple services.