uv - The Python Package Manager You've Been Waiting For

Level: beginner to intermediate
Prerequisites: basic knowledge of Python and the command line


The Problem uv Solves

If you’ve worked with Python for any amount of time, you’ve probably encountered something like this:

BASH
# To start a "simple" project, you needed:
pip install pipx          # to safely install global tools
pipx install poetry       # to manage dependencies
poetry self add poetry-plugin-shell  # to activate the venv via poetry
poetry python install 3.12           # to install the Python version
poetry new my-project     # to create the project
poetry shell              # to activate the environment
poetry add fastapi        # finally, install what you actually wanted
Click to expand and view more

Before writing a single line of code, you’d already had to install 3 separate tools (pip, pipx, poetry) and run 7 commands. And that’s not counting pyenv for managing multiple Python versions, pip-tools for locking dependencies, or virtualenv for creating virtual environments.

uv was built to fix this. A single tool, written in Rust, that replaces all of the above — and does it much faster.


What is uv?

uv is a Python package and project manager developed by Astral — the same company behind Ruff , the fastest Python linter/formatter available.

In one sentence: uv is to Python what Cargo is to Rust — a unified tool that handles everything in the project lifecycle.

What it replaces

Old toolPurposeuv equivalent
pipInstall packagesuv pip install
pip-toolsLock dependenciesuv pip compile
virtualenv / venvCreate virtual environmentsuv venv
pyenvManage Python versionsuv python install
poetryManage projects and depsuv init + uv add
pipxIsolated global toolsuv tool install / uvx
twinePublish packages to PyPIuv publish

Why Is It So Fast?

uv is written in Rust and uses several techniques that make it 10 to 100x faster than pip:

To put it concretely: installing Trio ’s dependencies takes ~11ms with a warm cache in uv, versus ~5s with pip.


Installation

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

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

# Via pip
pip install uv

# Via Homebrew (macOS)
brew install uv
Click to expand and view more

After installing, reload your shell:

BASH
source ~/.bashrc   # or ~/.zshrc
Click to expand and view more

Verify the installation:

BASH
uv --version
# uv 0.5.x (...)
Click to expand and view more

Core Concepts

Before diving into commands, two central concepts are worth understanding:

1. pyproject.toml — the heart of the project

pyproject.toml is the modern Python standard (PEP 517/518) for configuring projects. uv reads and writes it automatically. This is where your dependencies, Python version, scripts, and more are declared.

TOML
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "fastapi[standard]>=0.115.0",
]

[dependency-groups]
dev = [
    "pytest>=8.0.0",
    "ruff>=0.9.0",
]
Click to expand and view more

2. uv.lock — the lockfile

uv.lock is generated automatically and records the exact versions of all dependencies (including transitive ones). This guarantees that every team member and CI server installs exactly the same packages.

Tip: commit uv.lock to git. Never edit it manually.


Practical Guide: From Zero to Project

Creating a new project

BASH
# Option 1: create the folder and initialize
uv init my-project
cd my-project

# Option 2: initialize in the current folder
mkdir my-project && cd my-project
uv init
Click to expand and view more

Generated structure:

PLAINTEXT
my-project/
├── main.py           ← example entry point
├── pyproject.toml    ← project configuration
├── README.md
└── .python-version   ← pinned Python version
Click to expand and view more

Installing a specific Python version

BASH
# List available versions
uv python list

# Install a version
uv python install 3.13

# Pin the version for this project (creates/updates .python-version)
uv python pin 3.13
Click to expand and view more

Adding dependencies

BASH
# Main dependency
uv add fastapi

# With extras (equivalent to pip install fastapi[standard])
uv add "fastapi[standard]"

# Development dependency
uv add --dev pytest ruff

# Specific version range
uv add "sqlalchemy>=2.0,<3.0"
Click to expand and view more

uv updates pyproject.toml and uv.lock automatically.

Installing existing dependencies

BASH
# Sync the environment with pyproject.toml / uv.lock
uv sync

# Without dev dependencies (useful in production)
uv sync --no-dev
Click to expand and view more

Removing dependencies

BASH
uv remove fastapi
Click to expand and view more

Running commands in the virtual environment

BASH
# Run the application
uv run fastapi dev my_project/app.py

# Run tests
uv run pytest

# Run a script
uv run python script.py

# Open the interactive interpreter
uv run python
Click to expand and view more

You don’t need to activate the venv manually. uv run handles it for you.

If you prefer to activate the venv for a terminal session:

BASH
source .venv/bin/activate   # Linux/macOS
.venv\Scripts\activate      # Windows
Click to expand and view more

Full Example: FastAPI Project

Let’s build a FastAPI project from scratch with uv:

BASH
# 1. Create the project
uv init fast-zero
cd fast-zero

# 2. Create the Python package
mkdir fast_zero
touch fast_zero/__init__.py

# 3. Install dependencies
uv add "fastapi[standard]"
uv add --dev pytest pytest-cov ruff taskipy

# 4. Run (after creating app.py as shown below)
uv run fastapi dev fast_zero/app.py
Click to expand and view more

Before running, create the file fast_zero/app.py with the following content:

PYTHON
from fastapi import FastAPI

app = FastAPI()


@app.get('/')
def read_root():
    return {'message': 'Hello, World!'}
Click to expand and view more

Configuring pyproject.toml:

TOML
# ─────────────────────────────────────────────
# [project] — metadata and main dependencies
# Essential project information and packages
# required to run in production.
# ─────────────────────────────────────────────
[project]
name = "fast-zero"          # project name (kebab-case by convention)
version = "0.1.0"           # current project version
requires-python = ">=3.12"  # minimum accepted Python version
dependencies = [
    # fastapi with the [standard] group, which includes uvicorn, httpx
    # and other useful extras for development and production
    "fastapi[standard]>=0.115.0",
]

# ─────────────────────────────────────────────
# [dependency-groups] — development dependencies
# Packages used only during development
# (tests, linting, formatting). Not included in production.
# ─────────────────────────────────────────────
[dependency-groups]
dev = [
    "pytest>=8.0.0",      # testing framework
    "pytest-cov>=6.0.0",  # plugin to measure test coverage
    "ruff>=0.9.0",        # linter and code formatter
    "taskipy>=1.14.0",    # task runner — shortcuts for project commands
]

# ─────────────────────────────────────────────
# [tool.ruff] — global Ruff configuration
# Defines behaviors that apply to both
# the linter and the formatter.
# ─────────────────────────────────────────────
[tool.ruff]
line-length = 79             # maximum characters per line (PEP 8 standard)
extend-exclude = ['migrations']  # ignores the migrations folder (auto-generated
                                 # code — we don't want ruff to touch it)

# ─────────────────────────────────────────────
# [tool.ruff.lint] — linter configuration
# Defines which best-practice rules ruff
# will check in the code.
# ─────────────────────────────────────────────
[tool.ruff.lint]
preview = true  # enables rules still in experimental phase
select = [
    'I',   # isort — checks alphabetical ordering of imports
    'F',   # pyflakes — detects common errors (unused variables, unnecessary imports)
    'E',   # pycodestyle errors — code style errors
    'W',   # pycodestyle warnings — code style warnings
    'PL',  # pylint — general Python best practices
    'PT',  # flake8-pytest — best practices specific to pytest tests
]

# ─────────────────────────────────────────────
# [tool.ruff.format] — formatter configuration
# Defines the automatic code formatting style.
# ─────────────────────────────────────────────
[tool.ruff.format]
preview = true          # enables experimental formatting behaviors
quote-style = 'single'  # uses single quotes instead of double (team preference)

# ─────────────────────────────────────────────
# [tool.pytest.ini_options] — pytest configuration
# Defines how pytest behaves when running tests.
# ─────────────────────────────────────────────
[tool.pytest.ini_options]
pythonpath = "."           # adds the project root to PYTHONPATH, enabling
                           # imports like "from fast_zero.app import app"
addopts = '-p no:warnings' # suppresses warnings from external libraries to
                           # keep test output clean

# ─────────────────────────────────────────────
# [tool.taskipy.tasks] — command shortcuts
# Defines tasks runnable with "uv run task <name>".
# Saves you from memorizing long flags and paths.
# ─────────────────────────────────────────────
[tool.taskipy.tasks]
lint   = 'ruff check'                        # checks best practices in the code
format = 'ruff format'                       # formats the code automatically
run    = 'fastapi dev fast_zero/app.py'      # starts the development server
test   = 'pytest -s -x --cov=fast_zero -vv' # runs tests with:
                                             #   -s: shows prints during tests
                                             #   -x: stops on first failure
                                             #   --cov=fast_zero: measures package coverage
                                             #   -vv: verbose output
Click to expand and view more

Running the tasks:

BASH
uv run task test
uv run task lint
uv run task run
Click to expand and view more

Global Tools (replaces pipx)

uv also manages global tools — those you use in the terminal but aren’t tied to any specific project.

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

# Run without installing (temporary environment, equivalent to pipx run)
uvx ruff check .
uvx black --check .

# List installed tools
uv tool list

# Upgrade
uv tool upgrade ruff

# Remove
uv tool uninstall black
Click to expand and view more

Scripts with Inline Dependencies

A lesser-known but very useful feature: uv can run Python scripts with dependencies declared in the file itself, without needing a full project.

PYTHON
# script.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
#   "httpx",
#   "rich",
# ]
# ///

import httpx
from rich import print

response = httpx.get("https://api.github.com/repos/astral-sh/uv")
print(response.json()["stargazers_count"])
Click to expand and view more
BASH
uv run script.py
# Automatically installs deps in an isolated env and runs the script
Click to expand and view more

This is great for utility scripts, automations, and sharing code without setting up an entire project.


Command Reference Table

CommandWhat it does
Project
uv init my-projectCreates folder and initializes project
uv initInitializes project in current folder
uv init --libCreates a library layout (with src/)
Dependencies
uv add fastapiAdds a main dependency
uv add "fastapi[standard]"Adds with extras
uv add --dev pytestAdds as a dev dependency
uv remove fastapiRemoves a dependency
uv syncInstalls everything from pyproject.toml (= poetry install)
uv sync --no-devInstalls production dependencies only
uv lockGenerates/updates uv.lock without installing
Running
uv run <command>Runs in the project’s virtual environment
uv run --with httpx script.pyRuns with a temporary extra package
Global tools
uvx ruff check .Runs a tool without installing (= pipx run)
uv tool install ruffInstalls a tool globally (= pipx install)
uv tool uninstall ruffRemoves a global tool
uv tool listLists installed global tools
uv tool upgrade ruffUpgrades a global tool
Python versions
uv python install 3.13Downloads and installs Python 3.13
uv python listLists available/installed versions
uv python pin 3.12Pins version in .python-version
uv python uninstall 3.11Removes an installed version
Virtual env and pip
uv venvCreates .venv/ in current folder
uv venv --python 3.12Creates venv with specific version
uv pip install fastapiInstalls a package (pip-compatible interface)
uv pip freezeLists installed packages
uv pip compile pyproject.toml -o requirements.txtGenerates a locked requirements.txt

uv vs. Alternatives

uv vs. pip + venv

pip + venvuv
SpeedSlow10–100x faster
LockfileNone (needs pip-tools)Native uv.lock
Manages PythonNoYes
Single toolNo (multiple tools)Yes
Learning curveLowLow

When to use pip + venv: legacy projects, environments where uv can’t be installed.


uv vs. Poetry

Poetryuv
SpeedModerateMuch faster
Manages PythonYes (via plugin)Yes (native)
Lockfile formatpoetry.lock (proprietary)uv.lock (PEP standard)
Publish to PyPIYesYes (uv publish)
Shell pluginNeeds installationuv run makes it unnecessary
MaturityHigh (since 2018)Growing fast (2024+)
pip compatibilityPartialFull (pip interface)

When to use Poetry: teams with heavy Poetry history, projects that depend on specific Poetry plugins.


uv vs. Conda

Condauv
Non-Python packagesYes (C, Fortran, CUDA)No
SpeedSlowMuch faster
Data science useExcellentAdequate
Installation sizeLargeSmall
FocusData science / MLGeneral Python development

When to use Conda: data science with heavy native dependencies (GPU TensorFlow, CUDA, geospatial packages).


Advantages of uv


Disadvantages and Limitations


Migrating from Poetry to uv

If you have an existing Poetry project and want to migrate:

BASH
# 1. Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. In your project directory, sync
# uv can read Poetry's pyproject.toml natively
uv sync

# 3. Swap your day-to-day commands
# poetry run pytest   →   uv run pytest
# poetry add X        →   uv add X
# poetry shell        →   source .venv/bin/activate  (or just use uv run)

# 4. (Optional) Generate uv.lock and remove poetry.lock
uv lock
git rm poetry.lock
git add uv.lock
Click to expand and view more

uv sync can read pyproject.toml files generated by Poetry. The migration is typically straightforward for most projects.


Day-to-Day Tips

Pass environment variables when running

BASH
DATABASE_URL=postgresql://localhost/dev uv run python manage.py migrate
Click to expand and view more

Working with .env files

BASH
# uv doesn't read .env automatically — use python-dotenv or direnv
uv add --dev python-dotenv
Click to expand and view more

Check what’s installed

BASH
uv pip list
uv pip show fastapi
Click to expand and view more

Upgrade all dependencies

BASH
uv lock --upgrade
uv sync
Click to expand and view more

Export requirements.txt (for legacy deployments)

BASH
uv pip compile pyproject.toml -o requirements.txt
# or, for production only:
uv export --no-dev -o requirements.txt
Click to expand and view more

Run a one-off command without a project

BASH
uv run --with fastapi --with uvicorn python -c "
import uvicorn
from fastapi import FastAPI
app = FastAPI()

@app.get('/')
def root(): return {'ok': True}

uvicorn.run(app)
"
Click to expand and view more

CI/CD Integration

GitHub Actions

YAML
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v4
        with:
          version: "latest"

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

      - name: Run tests
        run: uv run pytest
Click to expand and view more

The --frozen flag ensures uv.lock won’t be updated during CI — if there’s a divergence, the build fails, which is the desired behavior.

Docker

DOCKERFILE
FROM python:3.13-slim

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /app

# Copy lockfile and install dependencies (cacheable layer)
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev

# Copy code
COPY . .

CMD ["uv", "run", "fastapi", "run", "fast_zero/app.py", "--port", "8000"]
Click to expand and view more

Conclusion

uv is probably the most significant improvement to the Python development experience in years. It doesn’t reinvent the wheel — it takes all the tools we were already using and unifies them into a coherent interface that’s dramatically faster and easier to use.

If you’re starting a new project today, use uv. If you have existing projects with Poetry or pip, the migration is straightforward and worth the effort.

The Python ecosystem finally has the tool it deserved.


Additional Resources

Start searching

Enter keywords to search articles

↑↓
ESC
⌘K Shortcut