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
# 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
uv pip install requests pandas
Output:
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 compilereplace those two tools. - Replacement for poetry (for many use cases):
uv init+uv add+uv rungive a poetry-like workflow.
If you're starting a new project from scratch,
uvis the recommended tool in 2026. For existing poetry projects, migration is low-effort but optional.
Common pitfalls
uv is not pip —
uv pip installinstalls into the active virtual environment, not the system Python. If no venv is active, uv installs into an auto-created project venv. Runuv venvfirst to be explicit.
uv runis notpython—uv run script.pyuses the project's venv and can install dependencies on the fly. It is not a direct replacement forpython script.pyin 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.
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:
Using CPython 3.12.3
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate
Richer example — project workflow
# 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:
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.
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:
uv python install 3.12
uv python install 3.13
uv python list # show available versions
Output:
Installed Python 3.12.3 in 8.4s
+ cpython-3.12.3-linux-x86_64-gnu
uv vs pip vs poetry
| Feature | pip | poetry | uv |
|---|---|---|---|
| Install speed | Slow | Slow | Fast (Rust) |
| Lockfile | ❌ (use pip-tools) | ✅ | ✅ (uv.lock) |
| Project management | ❌ | ✅ | ✅ |
| Python version mgmt | ❌ | ❌ | ✅ |
pyproject.toml support | Partial | ✅ | ✅ |
| 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.
| Concern | uv pip | Native (uv add, uv sync, …) |
|---|---|---|
Reads pyproject.toml | No | Yes |
Updates uv.lock | No | Yes |
Manages .venv | Only with active venv | Auto-creates and manages |
| Drop-in for pip | Yes | No (different surface) |
| Best for | Ad-hoc installs, legacy scripts | Project 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.
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:
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.
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:
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.
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:
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].
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:
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.
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:
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.
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:
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.
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:
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.
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:
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.
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:
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/*.
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:
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.
# /// 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:
uv run check_stars.py
Output:
Reading inline script metadata from `check_stars.py`
Installed 4 packages in 80ms
42017
Manage script metadata without editing the file by hand:
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:
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.
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.locklikepackage-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.
[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:
[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.
# 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:
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.
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:
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).
[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.
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:
/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.
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:
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
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:
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
| Feature | uv | poetry | pdm | rye |
|---|---|---|---|---|
| Language | Rust | Python | Python | Rust |
| Lockfile | uv.lock (universal) | poetry.lock | pdm.lock | requirements.lock |
| Project workflow | uv add/sync | poetry add/install | pdm add/install | rye add/sync |
| Python download | Yes | No | Yes (via pdm python install) | Yes |
| Workspace | Yes | Yes (plugin) | Yes | Yes |
| PEP 621 metadata | Native | Plugin/legacy split | Native | Native |
| PEP 723 scripts | Yes | No | No | Yes |
| Build backend | Any PEP 517 | poetry-core (or any) | pdm-backend (or any) | Any PEP 517 |
| Drop-in pip surface | uv pip | No | pdm (limited) | No |
| Tool installer | uv tool (pipx-like) | No | No | rye tools |
| Speed | Fastest | Slow | Moderate | Fast |
Astral acquired Rye in 2024; new development concentrates on uv. Rye remains stable but is no longer the recommended choice.
Common pitfalls (additional)
uv pipdoes not updatepyproject.toml— onlyuv adddoes. If you useuv pip install pandasin a uv-managed project,uv syncwill later remove pandas because it isn't in the lock.
uv.lockconflicts on merge — when two branches both runuv add, the resulting lockfile conflicts are messy. Resolve by accepting one side and runninguv lockagain to regenerate.
Hard-links across filesystems fail — Docker images, network mounts, and tmpfs do not support hard-links. Set
UV_LINK_MODE=copyor pass--link-mode copy.
uv runinside an already-activated venv —uv runstill resyncs the project venv, which may not be the one you activated. Either fully commit touv run(don't activate) or use plainpythonafter activation.
Pin uv itself in CI:
astral-sh/setup-uv@v3withversion: "0.4.18". uv minor releases occasionally change defaults (e.g., resolver behavior).
Use
uv cache prune --cito 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
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:
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
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:
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
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
- 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
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:
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
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:
Reading inline script metadata from `fetch_weather.py`
Installed 8 packages in 142ms
{'status': 'OK', 'forecast': 'https://api.weather.gov/gridpoints/...', 'alerts': []}
Environment variables
| Variable | Purpose |
|---|---|
UV_CACHE_DIR | Override cache location |
UV_PYTHON | Default Python to use (3.12, pypy@3.10, /path/to/python) |
UV_PYTHON_PREFERENCE | managed / system / only-managed / only-system |
UV_INDEX_URL | Primary package index |
UV_EXTRA_INDEX_URL | Additional indexes (space-separated) |
UV_NO_INDEX | Disable PyPI completely |
UV_LINK_MODE | hardlink / copy / symlink / clone |
UV_COMPILE_BYTECODE | 1 to pre-compile .pyc on install |
UV_FROZEN | 1 to default --frozen |
UV_OFFLINE | 1 to never touch the network |
UV_PROJECT_ENVIRONMENT | Override venv path (default .venv) |
UV_PUBLISH_TOKEN | API token for uv publish |
UV_HTTP_TIMEOUT | Network timeout in seconds (default 30) |
UV_CONCURRENT_DOWNLOADS | Cap parallel downloads |
UV_NATIVE_TLS | Use OS-native TLS stack (corporate proxies) |