cheat sheet

uv

Install packages and manage virtual environments blazingly fast with uv. Covers uv pip, uv venv, uv run, uv init, and how it compares to pip and poetry.

uv — Fast Python Package Manager

What it is

uv is a Python package installer and resolver written in Rust by Astral (the same team that makes ruff). It is a drop-in replacement for pip, pip-tools, and virtualenv that runs 10–100× faster. It also supports project-level workflows (uv init, uv add, uv run) as an alternative to poetry.

Install

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

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

# Or via pip (if you already have Python)
pip install uv

Output: (none — exits 0 on success)

Quick example — install packages

bash
uv pip install requests pandas

Output:

text
Resolved 7 packages in 312ms
Installed 7 packages in 143ms
 + certifi==2024.2.2
 + charset-normalizer==3.3.2
 + idna==3.7
 + numpy==2.0.0
 + pandas==2.2.2
 + python-dateutil==2.9.0
 + requests==2.32.3

When / why to use it

  • Replacement for pip: faster resolution, better error messages, compatible commands.
  • Replacement for virtualenv + pip-tools: uv venv + uv pip compile replace those two tools.
  • Replacement for poetry (for many use cases): uv init + uv add + uv run give a poetry-like workflow.

If you're starting a new project from scratch, uv is the recommended tool in 2026. For existing poetry projects, migration is low-effort but optional.

Common pitfalls

uv is not pipuv pip install installs into the active virtual environment, not the system Python. If no venv is active, uv installs into an auto-created project venv. Run uv venv first to be explicit.

uv run is not pythonuv run script.py uses the project's venv and can install dependencies on the fly. It is not a direct replacement for python script.py in all contexts.

Virtual environments

uv venv creates an isolated Python environment in .venv — the same structure as python -m venv but faster. Activate it normally with source .venv/bin/activate; subsequent uv pip install commands target the active venv automatically.

bash
uv venv                     # creates .venv in current directory
uv venv --python 3.12       # specify Python version
source .venv/bin/activate   # activate (Linux/macOS)
.venv\Scripts\activate      # activate (Windows)

Output:

text
Using CPython 3.12.3
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate

Richer example — project workflow

bash
# Initialize a new project
uv init myapp
cd myapp

# Add dependencies
uv add fastapi "uvicorn[standard]"
uv add --dev pytest ruff mypy

# Run a script
uv run uvicorn main:app --reload

# Sync dependencies from uv.lock
uv sync

# Show what's installed
uv pip list

Output:

text
Initialized project `myapp` at `/home/user/myapp`

Resolved 18 packages in 1.2s
Installed 18 packages in 380ms
 + annotated-types==0.7.0
 + anyio==4.4.0
 + fastapi==0.111.1
 + uvicorn==0.30.1
 + ...

INFO:     Started server process [12345]
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Drop-in pip commands

uv pip mirrors the pip subcommand surface — install, uninstall, list, freeze, compile — but resolves and installs packages 10–100× faster. Swap pip for uv pip in existing scripts and CI workflows with no other changes.

bash
uv pip install <pkg>                  # install
uv pip install -r requirements.txt    # from file
uv pip install -e .                   # editable install
uv pip uninstall <pkg>
uv pip list
uv pip freeze > requirements.txt
uv pip compile requirements.in        # pin transitive deps → requirements.txt

Output: (none — exits 0 on success)

Install Python versions

uv can download and manage Python interpreters:

bash
uv python install 3.12
uv python install 3.13
uv python list            # show available versions

Output:

text
Installed Python 3.12.3 in 8.4s
 + cpython-3.12.3-linux-x86_64-gnu

uv vs pip vs poetry

Featurepippoetryuv
Install speedSlowSlowFast (Rust)
Lockfile❌ (use pip-tools)✅ (uv.lock)
Project management
Python version mgmt
pyproject.toml supportPartial
Drop-in for pip

Two surfaces: uv pip vs native commands

uv exposes two distinct command surfaces. uv pip mirrors pip's behavior and operates on whatever environment is active — it does not read pyproject.toml, does not update uv.lock, and treats packages as standalone installs. The native commands (uv add, uv sync, uv lock, uv run) are project-aware: they read pyproject.toml, update uv.lock, and manage the project's .venv automatically.

Use uv pip when you want a drop-in replacement for pip in existing scripts or CI. Use native commands when you want uv to manage the whole project lifecycle.

Concernuv pipNative (uv add, uv sync, …)
Reads pyproject.tomlNoYes
Updates uv.lockNoYes
Manages .venvOnly with active venvAuto-creates and manages
Drop-in for pipYesNo (different surface)
Best forAd-hoc installs, legacy scriptsProject workflow

uv python — interpreter management

uv can download, list, pin, and switch between CPython and PyPy interpreters without pyenv. Interpreters live under ~/.local/share/uv/python/ and are reused across projects.

bash
uv python install 3.12               # download CPython 3.12 (latest patch)
uv python install 3.12.4             # exact patch version
uv python install 3.11 3.12 3.13     # multiple versions at once
uv python install pypy@3.10          # PyPy
uv python install --reinstall 3.12   # force re-download

uv python list                        # all available versions (downloadable + installed)
uv python list --installed-only       # only what's already on disk
uv python find 3.12                   # show path to the matching interpreter

uv python pin 3.12                    # write .python-version in current dir
uv python uninstall 3.11              # remove an installed version

Output of uv python list --installed-only:

text
cpython-3.13.0-macos-aarch64-none    /Users/alice/.local/share/uv/python/cpython-3.13.0/bin/python3.13
cpython-3.12.4-macos-aarch64-none    /Users/alice/.local/share/uv/python/cpython-3.12.4/bin/python3.12
cpython-3.11.9-macos-aarch64-none    /Users/alice/.local/share/uv/python/cpython-3.11.9/bin/python3.11

The .python-version file is honored by uv venv and uv run — pin once per project and the rest of the team gets the same interpreter automatically.

uv venv — virtual environments

uv venv creates a virtual environment using uv's bundled or system Python, ~20× faster than python -m venv. Unlike pip's venv, uv does not need pip installed inside the venv — it bypasses it entirely.

bash
uv venv                         # .venv in current dir with default Python
uv venv --python 3.12           # specific version
uv venv --python 3.12.4         # exact patch
uv venv --python pypy@3.10      # PyPy
uv venv myenv                   # custom path
uv venv --system-site-packages  # inherit system site-packages
uv venv --seed                  # include pip / setuptools / wheel (uncommon)
uv venv --allow-existing        # don't error if .venv already exists
uv venv --clear                 # delete and recreate

Output:

text
Using CPython 3.12.4
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate

uv automatically discovers .venv and venv in the project root for subsequent commands — you do not need to activate the venv to use uv pip install, uv add, or uv run.

uv run — run commands without activation

uv run executes a command inside the project's virtual environment, ensuring dependencies are installed first. It is the recommended way to invoke tools — no source .venv/bin/activate needed.

bash
uv run python script.py
uv run pytest
uv run uvicorn main:app --reload
uv run -- python -m mypackage --flag value   # use -- to separate flags from uv

# Add a transient dependency for one command
uv run --with httpx python -c "import httpx; print(httpx.__version__)"

# Run with a different Python
uv run --python 3.11 pytest

# Run a script that has no project (treats it as a standalone)
uv run --no-project script.py

Output:

text
Installed 3 packages in 80ms
0.27.0

uv run does three things on each invocation: (1) syncs uv.lock if dependencies are out of date, (2) ensures the venv is current, (3) execs the command with the venv on PATH.

uv init — start a new project

uv init scaffolds a pyproject.toml, a starter Python module, a .gitignore, and a README.md. It detects whether you want an application or a library and writes the right [build-system].

bash
uv init                          # init in current empty dir
uv init myapp                    # create myapp/ and init inside
uv init --app                    # application layout (default)
uv init --lib                    # library layout (adds src/ and build backend)
uv init --package                # package layout — installable like a library
uv init --python 3.12            # pin Python version in pyproject.toml
uv init --no-readme              # skip README.md
uv init --no-pin-python          # don't write .python-version
uv init --vcs git                # initialize git (default)

Output of uv init --lib mylib:

text
Initialized project `mylib` at `/home/alice/mylib`
Created src/mylib/__init__.py
Created tests/
Created pyproject.toml
Created README.md
Created .python-version (3.12)
Initialized Git repository

uv add / uv remove — manage dependencies

uv add installs a package, updates pyproject.toml, and refreshes uv.lock in one step. uv remove is the inverse.

bash
uv add requests                              # default group
uv add "fastapi>=0.111"                      # version constraint
uv add "uvicorn[standard]"                   # with extras
uv add --dev pytest mypy ruff                # dev group
uv add --group docs mkdocs                   # arbitrary group name
uv add --optional plotting matplotlib        # optional dependency (extras)
uv add --editable ./libs/mylib               # editable install
uv add git+https://github.com/owner/repo     # git source
uv add --tag v1.2.3 git+https://github.com/owner/repo
uv add --branch main git+https://github.com/owner/repo
uv add --path ./local-pkg                    # local path
uv add --index-url https://my.pypi/simple/ private-pkg

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

Output of uv add fastapi:

text
Resolved 14 packages in 220ms
      Built 0 packages in 0.0s
   Installed 14 packages in 95ms
 + annotated-types==0.7.0
 + anyio==4.4.0
 + fastapi==0.111.1
 + idna==3.7
 + pydantic==2.7.4
 + pydantic-core==2.18.4
 + sniffio==1.3.1
 + starlette==0.37.2
 + typing-extensions==4.12.2

uv add is preferred over uv pip install for project work: it keeps pyproject.toml, uv.lock, and the installed environment in lockstep.

uv sync — install from lockfile

uv sync ensures the project's .venv matches uv.lock exactly — installing missing packages, upgrading stale ones, and removing anything not in the lock. Use it in CI, in Docker, and when checking out an existing branch.

bash
uv sync                          # install all groups
uv sync --frozen                 # require uv.lock to be up-to-date; fail otherwise
uv sync --locked                 # like --frozen but also re-verify the lock
uv sync --no-dev                 # production install (skip dev group)
uv sync --only-dev               # only dev dependencies
uv sync --extra plotting         # include an optional dependency
uv sync --all-extras             # all optional deps
uv sync --group docs             # include a named group
uv sync --upgrade                # upgrade all deps (rewrites uv.lock)
uv sync --upgrade-package fastapi    # upgrade just one
uv sync --no-install-project     # install deps but not the project itself
uv sync --inexact                # don't remove packages not in lock

Output:

text
Resolved 42 packages in 8ms
Audited 42 packages in 1ms

--frozen is the right flag for CI. It guarantees no resolution happens during install, so a stale uv.lock becomes a build failure rather than silently updating.

uv lock — resolve without installing

uv lock updates uv.lock from pyproject.toml without touching the environment. Useful when reviewing what changed before syncing, or in CI jobs that only need the lock.

bash
uv lock                                       # update uv.lock
uv lock --upgrade                             # upgrade all deps
uv lock --upgrade-package fastapi             # upgrade just fastapi
uv lock --check                               # error if lock is stale
uv lock --refresh                             # ignore caches; refetch metadata
uv lock --no-deps                             # only the project, no transitive resolution

Output:

text
Resolved 42 packages in 320ms

uv lock --check is the right command for a "lock is fresh" CI check — it exits non-zero if pyproject.toml would produce a different uv.lock.

uv tree — inspect the dependency graph

uv tree prints the project's dependency tree, similar to pipdeptree. Useful for diagnosing version conflicts and oversized graphs.

bash
uv tree
uv tree --depth 1                # only direct deps
uv tree --package fastapi        # only show fastapi's tree
uv tree --invert --package pydantic   # what depends on pydantic?
uv tree --no-dev                 # exclude dev group
uv tree --outdated               # mark packages with newer versions

Output:

text
myapp v0.1.0
├── fastapi v0.111.1
│   ├── pydantic v2.7.4
│   │   ├── annotated-types v0.7.0
│   │   ├── pydantic-core v2.18.4
│   │   └── typing-extensions v4.12.2
│   └── starlette v0.37.2
│       └── anyio v4.4.0
│           ├── idna v3.7
│           └── sniffio v1.3.1
└── (dev) pytest v8.2.2

uv build — build sdist and wheel

uv build invokes the project's build backend (declared in [build-system]) to produce a source distribution (.tar.gz) and a wheel (.whl) under dist/. The result is identical to what python -m build would produce.

bash
uv build                         # build sdist + wheel
uv build --sdist                 # sdist only
uv build --wheel                 # wheel only
uv build --out-dir build-out     # custom output directory
uv build --package mylib         # in a workspace, build a specific member

Output:

text
Building source distribution...
Successfully built dist/mylib-0.1.0.tar.gz
Building wheel from source distribution...
Successfully built dist/mylib-0.1.0-py3-none-any.whl

uv publish — upload to PyPI

uv publish uploads built distributions to PyPI (or another configured index). It is a thin wrapper around the PyPI upload API — equivalent to twine upload dist/*.

bash
uv publish                                              # upload everything in dist/
uv publish dist/mylib-0.1.0*                            # upload specific files
uv publish --index pypi                                 # named index
uv publish --token "pypi-AgEIcHl..."                    # API token
uv publish --publish-url https://test.pypi.org/legacy/  # TestPyPI

# Recommended: store token in env
export UV_PUBLISH_TOKEN="pypi-AgEIcHl..."
uv publish

Output:

text
Publishing 2 files to https://upload.pypi.org/legacy/
Uploading mylib-0.1.0.tar.gz (15 KB)
Uploading mylib-0.1.0-py3-none-any.whl (12 KB)

PEP 723 — single-file scripts with inline metadata

PEP 723 lets a Python script declare its dependencies in a TOML block at the top of the file. uv run reads that block and creates an ephemeral environment on the fly — no project, no pyproject.toml, no manual venv.

python
# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "httpx>=0.27",
#     "rich",
# ]
# ///
import httpx
from rich import print
r = httpx.get("https://api.github.com/repos/astral-sh/uv")
print(r.json()["stargazers_count"])

Run it:

bash
uv run check_stars.py

Output:

text
Reading inline script metadata from `check_stars.py`
Installed 4 packages in 80ms
42017

Manage script metadata without editing the file by hand:

bash
uv add --script check_stars.py "httpx>=0.27"
uv add --script check_stars.py --dev pytest
uv remove --script check_stars.py rich
uv lock --script check_stars.py    # writes a lockfile next to the script

Output:

text
Updated `check_stars.py`
Updated `check_stars.py`
Updated `check_stars.py`
Resolved 4 packages in 142ms

Use cases: CI helpers, one-off automation, gists, tutorials — anything where a pyproject.toml would be overkill.

uv.lock — file format and semantics

uv.lock is a TOML file written automatically by uv add, uv sync, and uv lock. It records the exact package versions, wheel URLs, and SHA-256 hashes resolved for every supported platform.

toml
version = 1
requires-python = ">=3.10"
resolution-markers = [
    "python_full_version >= '3.10' and sys_platform == 'darwin'",
    "python_full_version >= '3.10' and sys_platform == 'linux'",
]

[[package]]
name = "fastapi"
version = "0.111.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "pydantic" },
    { name = "starlette" },
]
[[package.wheels]]
url = "https://files.pythonhosted.org/packages/.../fastapi-0.111.1-py3-none-any.whl"
hash = "sha256:..."
size = 92110

Key properties:

  • Universal: one lock file covers every platform declared via tool.uv.environments.
  • Hash-checked: every wheel and sdist URL includes a SHA-256.
  • Reproducible: identical inputs produce identical lockfiles bit-for-bit.
  • Committed to git: always check it in. Treat uv.lock like package-lock.json.

Platform-specific resolution

By default uv.lock resolves for the current platform. To support multiple targets — e.g., devs on macOS, CI on Linux, production on linux/arm64 — declare the environments in pyproject.toml.

toml
[tool.uv]
environments = [
    "sys_platform == 'darwin' and platform_machine == 'arm64'",
    "sys_platform == 'linux' and platform_machine == 'x86_64'",
    "sys_platform == 'linux' and platform_machine == 'aarch64'",
]

uv resolves to the intersection of all environments, picking wheels that work on each. Re-run uv lock after editing this list.

For platform-conditional dependencies, use PEP 508 markers in pyproject.toml:

toml
[project]
dependencies = [
    "pywin32 ; sys_platform == 'win32'",
    "uvloop ; sys_platform != 'win32'",
]

Workspaces

A uv workspace lets one pyproject.toml manage multiple Python packages in a monorepo — each with its own pyproject.toml and source tree, sharing a single .venv and uv.lock.

toml
# Root pyproject.toml
[project]
name = "myorg-workspace"
version = "0"
requires-python = ">=3.12"

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

[tool.uv.sources]
shared-utils = { workspace = true }

Each member is a regular package:

bash
myorg-workspace/
├── pyproject.toml          ← workspace root
├── uv.lock                  ← single lockfile
├── .venv/                   ← single venv
└── packages/
    ├── api/
    │   ├── pyproject.toml
    │   └── src/api/
    ├── worker/
    │   ├── pyproject.toml
    │   └── src/worker/
    └── shared-utils/
        ├── pyproject.toml
        └── src/shared_utils/

Cross-member deps are resolved as editable installs automatically — change shared-utils source, and api sees the change without reinstall.

bash
uv sync                           # install entire workspace
uv sync --package api             # install just one member
uv run --package api uvicorn app:main   # run inside one member
uv build --package shared-utils   # build one member

Output:

text
Resolved 38 packages in 412ms
Installed 38 packages in 1.21s
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Successfully built dist/shared_utils-0.1.0-py3-none-any.whl

Configuration: pyproject.toml and uv.toml

uv reads project-level config from [tool.uv] in pyproject.toml, or from a standalone uv.toml at the project root (or ~/.config/uv/uv.toml for user-wide settings).

toml
[tool.uv]
# Pin Python interpreter source preference
python-preference = "managed"          # managed | system | only-managed | only-system
python-downloads = "automatic"         # automatic | manual | never

# Lockfile behavior
resolution = "highest"                 # highest | lowest | lowest-direct
prerelease = "if-necessary"            # disallow | allow | if-necessary | explicit

# Package sources
index-url = "https://pypi.org/simple"
extra-index-url = ["https://pypi.mycompany.com/simple"]
no-binary-package = ["pillow"]         # always build from source
only-binary-package = ["numpy"]        # never build from source

# Environments
environments = [
    "sys_platform == 'darwin'",
    "sys_platform == 'linux'",
]

# Constraints
constraint-dependencies = ["urllib3<2"]
override-dependencies = ["typing-extensions>=4.10"]

[tool.uv.sources]
mylib = { workspace = true }
private-tool = { git = "https://github.com/owner/private-tool", tag = "v1.0" }

[[tool.uv.index]]
name = "private"
url = "https://pypi.mycompany.com/simple"
explicit = true                        # only used when explicitly referenced

Environment variables override config for one-off use: UV_INDEX_URL, UV_EXTRA_INDEX_URL, UV_PYTHON, UV_CACHE_DIR, UV_LINK_MODE, UV_COMPILE_BYTECODE.

Caching

uv keeps a content-addressed global cache at ~/.cache/uv (Linux/macOS) or %LOCALAPPDATA%\uv\cache (Windows). It deduplicates wheels and sdists across every project on the machine.

bash
uv cache dir                  # show cache directory
uv cache clean                # remove everything
uv cache clean requests       # remove cache entries for one package
uv cache prune                # remove unused entries (keeps recently used)

Output of uv cache dir:

text
/Users/alice/Library/Caches/uv

Inside a venv, uv installs packages by hard-linking from the cache (or reflink on APFS/Btrfs) — so installing 100 packages costs almost no disk space if they're already cached.

UV_LINK_MODE controls this: hardlink (default), copy (required in Docker), symlink, clone (APFS/Btrfs reflinks).

Tool management — uv tool

uv tool installs Python applications (ruff, black, mypy) into isolated environments and exposes their CLIs on PATH. It is uv's answer to pipx.

bash
uv tool install ruff
uv tool install black
uv tool install "mypy[reports]"
uv tool install --python 3.11 some-tool

uv tool list
uv tool upgrade ruff
uv tool upgrade --all
uv tool uninstall ruff

# Run a tool without installing it
uv tool run ruff check .
uvx ruff check .              # short alias for `uv tool run`

Output of uv tool install ruff:

text
Resolved 1 package in 8ms
Installed 1 package in 12ms
 + ruff==0.5.5
Installed 1 executable: ruff

uvx is the fastest way to try a tool one-off — like npx for Python. The first run downloads; subsequent runs hit the cache and start instantly.

Self-update and shell completions

bash
uv self update                # upgrade uv itself to latest
uv self update 0.4.18         # pin a version
uv version                    # show installed version
uv --version                  # short alias

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
uv generate-shell-completion powershell >> $PROFILE

Output:

text
Upgrading uv to 0.4.18 from 0.4.16
Installed uv 0.4.18
uv 0.4.18 (commit abc123def 2026-05-20)

Comparison with poetry, pdm, rye

Featureuvpoetrypdmrye
LanguageRustPythonPythonRust
Lockfileuv.lock (universal)poetry.lockpdm.lockrequirements.lock
Project workflowuv add/syncpoetry add/installpdm add/installrye add/sync
Python downloadYesNoYes (via pdm python install)Yes
WorkspaceYesYes (plugin)YesYes
PEP 621 metadataNativePlugin/legacy splitNativeNative
PEP 723 scriptsYesNoNoYes
Build backendAny PEP 517poetry-core (or any)pdm-backend (or any)Any PEP 517
Drop-in pip surfaceuv pipNopdm (limited)No
Tool installeruv tool (pipx-like)NoNorye tools
SpeedFastestSlowModerateFast

Astral acquired Rye in 2024; new development concentrates on uv. Rye remains stable but is no longer the recommended choice.

Common pitfalls (additional)

uv pip does not update pyproject.toml — only uv add does. If you use uv pip install pandas in a uv-managed project, uv sync will later remove pandas because it isn't in the lock.

uv.lock conflicts on merge — when two branches both run uv add, the resulting lockfile conflicts are messy. Resolve by accepting one side and running uv lock again to regenerate.

Hard-links across filesystems fail — Docker images, network mounts, and tmpfs do not support hard-links. Set UV_LINK_MODE=copy or pass --link-mode copy.

uv run inside an already-activated venvuv run still resyncs the project venv, which may not be the one you activated. Either fully commit to uv run (don't activate) or use plain python after activation.

Pin uv itself in CI: astral-sh/setup-uv@v3 with version: "0.4.18". uv minor releases occasionally change defaults (e.g., resolver behavior).

Use uv cache prune --ci to reduce cache size in CI workflows — it keeps only the cache entries used by the current resolve.

Real-world recipes

Recipe — bootstrap a FastAPI service

bash
uv init --app myapi
cd myapi
uv add "fastapi>=0.111" "uvicorn[standard]" "pydantic>=2"
uv add --dev pytest httpx pytest-asyncio ruff mypy
uv run uvicorn main:app --reload

Output:

text
Initialized project `myapi` at `/home/alice/code/myapi`
Resolved 14 packages in 184ms
Installed 14 packages in 412ms
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [12345] using StatReload

Recipe — replace tox for a matrix of Python versions

bash
uv python install 3.10 3.11 3.12 3.13
for V in 3.10 3.11 3.12 3.13; do
  uv venv --python "$V" --clear ".venv-$V"
  uv sync --frozen --python ".venv-$V/bin/python"
  uv run --python ".venv-$V/bin/python" pytest
done

Output:

text
Installed 4 versions in 4.81s
============================== 42 passed in 1.21s ==============================
============================== 42 passed in 1.14s ==============================
============================== 42 passed in 1.07s ==============================
============================== 42 passed in 1.02s ==============================

Recipe — produce a requirements.txt from uv.lock for legacy tooling

bash
uv export --format requirements-txt --no-dev --frozen > requirements.txt
uv export --format requirements-txt --frozen > requirements-dev.txt

Output: (none — exits 0 on success)

Recipe — fast GitHub Actions cache

yaml
- name: Install uv
  uses: astral-sh/setup-uv@v3
  with:
    version: "0.4.18"
    enable-cache: true
    cache-dependency-glob: "uv.lock"

- name: Set up Python
  run: uv python install 3.12

- name: Install deps
  run: uv sync --frozen --no-dev

The action caches ~/.cache/uv keyed on the hash of uv.lock — subsequent runs install in <2s.

Recipe — workspace with shared utilities

bash
mkdir -p packages/api packages/worker packages/utils
uv init --no-readme
# edit root pyproject.toml to add [tool.uv.workspace]
uv init --lib packages/utils
uv init --app packages/api
uv init --app packages/worker

# Inside api/pyproject.toml
# [project]
# dependencies = ["shared-utils"]
# [tool.uv.sources]
# shared-utils = { workspace = true }

uv sync
uv run --package api uvicorn app:main

Output:

text
Initialized project at /home/alice/code/myorg-workspace
Initialized library `utils` at packages/utils
Initialized project `api` at packages/api
Initialized project `worker` at packages/worker
Resolved 12 packages in 248ms
Installed 12 packages in 612ms
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Recipe — one-off script with inline deps

bash
cat > fetch_weather.py <<'EOF'
# /// script
# requires-python = ">=3.12"
# dependencies = ["httpx", "rich"]
# ///
import httpx
from rich import print
print(httpx.get("https://api.weather.gov/").json())
EOF

uv run fetch_weather.py

Output:

text
Reading inline script metadata from `fetch_weather.py`
Installed 8 packages in 142ms
{'status': 'OK', 'forecast': 'https://api.weather.gov/gridpoints/...', 'alerts': []}

Environment variables

VariablePurpose
UV_CACHE_DIROverride cache location
UV_PYTHONDefault Python to use (3.12, pypy@3.10, /path/to/python)
UV_PYTHON_PREFERENCEmanaged / system / only-managed / only-system
UV_INDEX_URLPrimary package index
UV_EXTRA_INDEX_URLAdditional indexes (space-separated)
UV_NO_INDEXDisable PyPI completely
UV_LINK_MODEhardlink / copy / symlink / clone
UV_COMPILE_BYTECODE1 to pre-compile .pyc on install
UV_FROZEN1 to default --frozen
UV_OFFLINE1 to never touch the network
UV_PROJECT_ENVIRONMENTOverride venv path (default .venv)
UV_PUBLISH_TOKENAPI token for uv publish
UV_HTTP_TIMEOUTNetwork timeout in seconds (default 30)
UV_CONCURRENT_DOWNLOADSCap parallel downloads
UV_NATIVE_TLSUse OS-native TLS stack (corporate proxies)