Uv A Complete Guide
Uv A Complete Guide
What is UV?
UV is a modern Python package and project manager written in Rust. It serves as a drop-in replacement for
traditional tools like pip , virtualenv , and pip-tools , aiming to simplify dependency management
with speed and reliability[datacamp.com]. In other words, uv is one tool that does the job of many – it can
create virtual environments, install packages, resolve dependencies, and even build and publish your Python
projects[docs.astral.sh]. It was developed by Astral (the team behind the Ruff linter) and first released in early
2024 as an ambitious attempt at a “Cargo for Python”[astral.sh][loopwerk.io]. The name “uv” is pronounced
“you-vee,” and the project has quickly gained attention for its blazing-fast performance and comprehensive
feature set.
Key Features of UV
UV comes packed with features designed to streamline Python development. Here are some of its key
capabilities:
Ultra-Fast Dependency Resolution and Installation: UV is built for speed. Thanks to its Rust
implementation and parallel processing, it can resolve and install packages 10–100× faster than pip in
many cases[github.com][dimasyotama.medium.com]. It uses a global cache and copy-on-write links to
avoid redundant downloads and save disk space[dimasyotama.medium.com]. This means even large
dependency graphs are handled quickly and efficiently.
Virtual Environment Management: UV automatically creates and manages virtual environments for your
projects. By default, it places a .venv directory in your project, just like Python’s standard
venv [mmngreco.dev]. You don’t need to manually run python -m venv or activate environments –
UV handles isolation behind the scenes. You can also use uv venv commands to create or manage
environments explicitly if needed.
Python Version Management: UV can install and manage multiple Python versions on your system. You
can install specific Python interpreters (CPython or PyPy) with uv python install , and even pin a
project to a particular version using uv python pin [docs.astral.sh]. This makes it easy to switch
between Python versions without using tools like pyenv . UV will automatically use the correct Python
for your project’s environment.
Declarative Dependencies with pyproject.toml : UV uses the standard pyproject.toml file (PEP
621) to declare project dependencies and metadata[datacamp.com]. You simply list your dependencies (and
optional groups) in this file. UV reads this to resolve and install packages, keeping your project
configuration in one place. It’s compatible with other tools that use pyproject.toml (like Poetry or
Pipenv), making it easy to adopt in existing projects[datacamp.com].
Lockfile for Reproducible Environments: UV generates a uv.lock file that pins down the exact
versions of all dependencies (and their sub-dependencies)[datacamp.com]. This lockfile ensures that every
installation (yours or a teammate’s or in CI) uses the same dependency versions, leading to consistent
environments. The lock is updated whenever you add/update packages. You can commit uv.lock to
version control for reproducibility.
Integrated Workflow Commands: UV provides a rich CLI for common tasks. For example:
uv add – add a package to your project (updates pyproject.toml and lockfile).
uv sync – install all dependencies from the lockfile into the environment (useful in fresh setups or
CI).
uv run – execute a command or script in the project’s environment (no need to activate the venv
first).
uv build – build your project into a package (sdist or wheel) for distribution.
uvx – a handy alias for running one-off tools in isolated environments (similar to pipx )
[github.com].
These commands cover the full lifecycle from development to deployment.
Compatibility and Extensibility: UV is designed to play well with the existing Python ecosystem. It
includes a uv pip subcommand that acts as a drop-in replacement for pip (with the same arguments),
giving you a familiar interface but with UV’s speed under the hood[docs.astral.sh]. You can use uv pip
install , uv pip freeze , etc., and even mix UV with requirements.txt workflows if needed.
UV also supports multiple build backends (like Hatchling, setuptools, etc.), so it can work with various
packaging setups[dimasyotama.medium.com]. Additionally, it has features like dependency overrides,
platform-independent resolution, and support for private package indexes, making it flexible for different
use cases[github.com].
All these features make UV a one-stop tool for Python project management. It aims to eliminate the need for
separate tools for virtual environments, dependency resolution, and publishing. As the developers put it, UV is
“a single tool to replace pip , pip-tools , pipx , poetry , pyenv , twine , virtualenv , and
more”[docs.astral.sh].
Speed and Efficiency: The most touted benefit of UV is its performance. Whether it’s resolving a complex
dependency graph or installing many packages, UV is significantly faster than traditional tools. This speed
can save a lot of time, especially in large projects or CI pipelines where environment setup can otherwise
drag on[fmind.medium.com]. For example, one MLOps team saw their CI dependency installation time
drop dramatically after switching from Poetry to UV[fmind.medium.com]. UV’s efficient caching and
parallel downloads mean even repeated installs stay fast.
Simplicity and Convenience: With UV, you can manage your entire project with a single tool and a single
configuration file ( pyproject.toml ). There’s no need to manually juggle virtual environments or
remember separate commands for pip , venv , etc. UV automates environment creation and ensures
you’re always working in the right context. This reduces cognitive overhead and setup steps. As one
developer noted, using UV is “satisfying” and feels like a “one tool to rule them all” approach to Python
packaging[linkedin.com].
Reproducibility and Reliability: The lockfile ( uv.lock ) guarantees that installations are reproducible.
This is crucial for collaboration and deployment – everyone gets the same versions of all packages. UV’s
dependency resolver is also designed to be reliable, finding compatible versions even in complex
dependency scenarios. It uses the PubGrub algorithm (the same as Dart/Flutter’s resolver) under the
hood, known for its correctness and speed[github.com]. This means fewer dependency conflicts and more
confidence that your project’s dependencies will work together.
Modern Packaging Standards: UV embraces modern Python packaging standards (PEP 621, PEP
517/518, etc.) rather than inventing its own. This means your pyproject.toml is standard and can be
understood by other tools. If you ever need to fall back to pip or use another package manager, your
setup remains compatible[datacamp.com]. UV also supports editable installs for development and has
built-in support for scripts and entry-points, aligning with Python’s best practices.
All-in-One Workflow: From initializing a project to publishing a package, UV covers the entire workflow.
You can uv init a new project, uv add dependencies as you develop, use uv run to execute
scripts or tests in the environment, uv build to create distribution packages, and uv publish to send
them to PyPI[docs.astral.sh]. This end-to-end integration is a big draw – you don’t need to switch between
venv , pip , setuptools , twine , etc. It’s all handled in a consistent way by UV.
Active Development and Community: UV is backed by a company (Astral) and has a growing
community. It’s being actively developed with frequent releases that add features and fix issues. The
project’s GitHub repository has gained thousands of stars and contributions, indicating strong community
interest[accelerated-ai.medium.com]. Many early adopters report positive experiences, and there are
already tutorials and guides on using UV in various contexts (data science, web apps, CI/CD, etc.). This
momentum means UV is likely to continue improving and gaining support.
Of course, no tool is perfect for every situation. We’ll discuss some potential drawbacks later. But for many
Python developers, UV’s combination of speed, convenience, and comprehensive feature set makes it a very
attractive choice. As one user summarized, “if your situation allows it, always try uv first. Then fall back on
something else if that doesn’t work out”[bitecode.dev] – the cost of trying UV is low, and the payoff in
productivity can be high.
To better understand UV’s place in the ecosystem, let’s compare it with some traditional Python package
management approaches:
UV vs. pip + venv: Using pip and venv manually is the standard way to manage Python
environments. However, it requires remembering multiple steps (create venv, activate it, use pip install,
manually manage requirements.txt , etc.). UV automates these steps – it creates the virtualenv for
you and runs pip under the hood with optimal settings. The result is that common operations are faster
and simpler with UV. For example, installing a package is just uv add package (which updates your
config and installs it), whereas with pip you’d do pip install package and then manually update
requirements.txt if you want to track it. UV’s lockfile also provides more deterministic control than
a typical requirements.txt (which often only lists top-level packages, not all sub-dependencies with
exact versions). In short, UV brings speed and automation to what is otherwise a manual, error-prone
process with pip/venv.
UV vs. pip-tools: pip-tools (which includes pip-compile and pip-sync ) is a popular way to get
deterministic builds with pip. It uses .in files for dependencies and compiles them into a full
requirements.txt lock. UV essentially replaces this workflow as well – uv lock can be seen as the
equivalent of pip-compile , generating a lock with all pinned versions, and uv sync replaces pip-
sync by installing exactly those versions. Users of pip-tools will find UV’s approach familiar but much
faster. UV’s resolver is more modern than pip’s resolver, so it can handle complex version constraints more
effectively. Also, UV doesn’t require separate .in files; it reads directly from pyproject.toml .
Migrating from pip-tools to UV is straightforward: you can point UV at your existing
requirements.in or just start using pyproject.toml [github.com].
UV vs. Poetry: Poetry is a well-known Python packaging tool that also uses pyproject.toml and a
lockfile. It has a similar high-level goal to UV (manage dependencies, environments, and publishing).
However, there are some differences. Speed: UV is generally faster than Poetry, especially for large
projects, due to its Rust implementation and parallel execution[dimasyotama.medium.com]. Many users
report that dependency resolution which could take minutes with Poetry takes only seconds with UV.
Workflow: Poetry and UV have comparable features (virtualenv management, dependency resolution,
build/publish), but UV’s CLI commands differ slightly. For example, to add a package, both use add , but
to update a package, UV uses uv lock --upgrade-package instead of a dedicated update
command[loopwerk.io]. Some find Poetry’s CLI more intuitive, while others like UV’s approach of
keeping core commands minimal and using flags for variations. Community and Maturity: Poetry has
been around longer and has a larger community and plugin ecosystem. UV is newer, so while its core is
solid, it may lack some of the niche integrations or battle-testing that Poetry has. That said, UV is actively
catching up. Standard compliance: Both use pyproject.toml , but Poetry historically used a custom
format for dependencies (with a [tool.poetry] section) whereas UV uses the standard [project]
and [tool.uv] sections as per PEP 621. This means a pyproject.toml for UV is more likely to be
usable by other tools without modification. In practice, many developers switching from Poetry to UV have
found the transition smooth and have welcomed UV’s speed improvements[fmind.medium.com].
UV vs. Pipenv: Pipenv was an earlier attempt to combine pip and virtualenv with a Pipfile and
Pipfile.lock. It gained popularity but has seen slower development in recent years. UV can be seen as a
more modern successor to Pipenv’s理念. UV’s resolver is more advanced than Pipenv’s (which used pip’s
old resolver), and UV provides better performance and features. Pipenv’s Pipfile is similar to
pyproject.toml , and in fact, UV can read a Pipfile if needed (though it encourages using
pyproject.toml ). Users have successfully migrated from Pipenv to UV, enjoying the speed boost and
more reliable resolution[medium.com]. One difference is that Pipenv had some conveniences for
environment variables and scripts, but UV covers those through standard means (e.g. using .env files or
uv run for scripts).
UV vs. Conda/Miniconda: Conda is a package manager for Python (and other languages) often used in
data science. It handles non-Python packages (like libraries written in C/C++), which UV does not – UV
only manages Python packages from PyPI. If you need things like specific NumPy builds or CUDA
libraries that Conda provides, UV won’t replace that. However, for pure Python projects, UV offers a much
faster and lighter alternative to Conda. Conda’s dependency solving can be notoriously slow, whereas UV’s
is quick. Also, Conda environments tend to be heavier (with many system libraries), whereas UV uses
standard Python virtual environments. Many data scientists have started using UV alongside or instead of
Conda for managing Python dependencies, especially when Conda’s extra capabilities aren’t
required[datacamp.com]. UV’s minimal footprint and speed can dramatically improve environment setup
times compared to Conda[datacamp.com].
In summary, UV stands out by combining the best ideas from existing tools (pip, poetry, pipenv, etc.) into
one fast, unified tool. It provides the dependency resolution and lockfile benefits of Poetry/Pipenv, the
environment management of venv/pip-tools, and adds its own speed and polish. For most Python projects, UV
can replace the hodgepodge of tools you might have been using and simplify your workflow.
How UV Works
Under the hood, UV is a Rust program that interfaces with Python’s packaging mechanisms. Here’s a high-
level overview of how UV operates:
Rust Implementation for Speed: Being written in Rust gives UV a performance edge. Rust is a systems
language known for its speed and memory safety. UV leverages Rust to handle tasks like parsing package
metadata, resolving dependencies, and downloading packages much faster than a Python-based tool would.
It can also easily use parallel threads (e.g. to download multiple packages at once or process metadata
concurrently). This is a big reason UV can resolve dependencies in milliseconds where pip might take
minutes[dimasyotama.medium.com]. The trade-off is that UV isn’t written in Python (so if you wanted to
contribute, you’d need Rust skills), but for end-users this is irrelevant – you just get a fast binary that works
on your system.
Global Cache and Installation: UV maintains a global cache of all the packages you
download[docs.astral.sh]. When you install a package, UV first checks if it’s already in the cache. If so, it
uses that copy (possibly via hardlinks or copy-on-write) to avoid redundant storage. This saves disk space
and time. Only new or updated packages get downloaded. Once the packages are ready, UV installs them
into your project’s virtual environment. It handles linking the packages in a way that’s efficient and isolated
to that environment. This approach is inspired by tools like pnpm in the Node.js world, which use a
similar global store for packages[github.com].
Dependency Resolution: UV’s resolver is a key part of its magic. It takes your project’s dependencies
(and their constraints) and finds a set of versions that satisfy all requirements. UV uses the PubGrub
algorithm, which is an efficient SAT-based resolver originally developed for Dart[github.com]. PubGrub is
known for quickly finding a solution even in complex cases, and it’s the same resolver that Poetry switched
to in recent versions. By using this algorithm, UV can often resolve dependencies without backtracking
and with minimal iterations. The result is that even large dependency graphs (like those of big web
frameworks or ML libraries) are resolved in a fraction of the time pip would take. UV also supports
multiple resolution strategies and can override dependencies if needed, giving advanced users
flexibility[github.com].
Virtual Environment Handling: When you work on a UV-managed project, UV ensures there’s a virtual
environment in place (typically at .venv ). It uses Python’s built-in venv module under the hood to
create this environment. You can see that .venv directory just like any other virtualenv, and you can even
activate it and use pip directly if you want (though UV discourages manual changes since it could
conflict with uv.lock ). UV’s uv run command will execute commands in the context of this
environment automatically. If you run a script or command that’s not part of a project, UV can even create
a temporary environment on the fly for it. This means UV can act like pipx for running one-off tools –
e.g. uvx pycowsay hello will download pycowsay in an isolated environment and run it, then you
never have to think about that environment again[github.com].
Integration with Python Packaging: UV doesn’t reinvent the wheel for building or installing packages –
it uses the standard Python packaging infrastructure. When you run uv install or uv sync , it’s
ultimately calling Python’s pip (or a Rust reimplementation of that logic) to install wheels or build
packages. For building distributions ( uv build ), UV uses the build package under the hood to create
sdist/wheel files[docs.astral.sh]. And uv publish uses twine (or a Rust equivalent) to upload to
PyPI[docs.astral.sh]. So UV is basically a high-level orchestrator that leverages existing Python tools in a
smart way. The difference is that UV coordinates these steps efficiently and provides a user-friendly
interface.
In essence, UV is a fast, opinionated wrapper around the Python packaging tools, with a smart resolver
and caching layer on top. It abstracts away a lot of the complexity of Python’s packaging system while
ensuring compatibility with it. This design means you get all the benefits of the standard Python ecosystem, but
with a smoother experience.
Installation and Setup
Getting started with UV is straightforward. You can install UV on Windows, macOS, or Linux. Here are the
common installation methods:
Standalone Installer (Recommended): UV provides a quick one-line installer for all platforms. On
macOS/Linux, you can use curl :
On Windows (PowerShell):
This will download and install the latest UV binary for your system. The standalone installer places UV in your
home directory (e.g. ~/.local/bin on Linux/macOS) and adds it to your PATH. It doesn’t require having
Python installed beforehand – it’s a self-contained Rust binary[docs.astral.sh]. This is the recommended way
because it’s simple and ensures you have the latest version.
Via pip or pipx: You can also install UV as a Python package (though this might seem ironic for a tool that
replaces pip). If you have Python and pip available, you can do:
pip install uv
pipx install uv
This will install UV into your Python environment. Keep in mind that UV itself is a Rust program, so this pip
package is basically a wrapper that downloads the correct UV binary for your platform. It’s convenient if you
already use pipx or want to manage UV through pip. After installation via pip, you can run uv --version
to verify it’s working.
Other Methods: UV can also be installed via package managers like Homebrew (on macOS: brew
install astral-sh/uv/uv ) or Scoop (on Windows). These package managers might not always have
the absolute latest version, but they provide an easy install if you prefer using them. There are also Docker
images available with UV pre-installed for container environments[docs.astral.sh].
Once installed, you can check the version and ensure UV is in your PATH:
uv --version
After installation, you’re ready to start using UV. For a new project, you might run uv init to create a new
project with a pyproject.toml . For an existing project, you can simply start using UV commands in that
directory (UV will detect or create a virtual environment there). UV will automatically install Python for you if
it’s not present – for example, if you run uv run python and don’t have Python installed, UV will fetch a
Python interpreter to use[docs.astral.sh]. This makes getting started extremely easy.
Updating UV: If you installed via the standalone installer, you can update UV to the latest version with:
uv self update
This command will check for updates and upgrade your UV binary if a newer version is
available[realpython.com]. If you installed via pip/pipx, you can use pip install --upgrade uv or
pipx upgrade uv instead. UV is under active development, so it’s good to keep it updated to get the latest
improvements.
UV provides a rich command-line interface. Here’s a quick reference for common commands and how they fit
into a typical workflow:
uv init [project-name]: Initializes a new Python project. If you provide a project name, it creates a new
directory with that name and sets up the project there; otherwise, it initializes in the current directory. This
command creates a pyproject.toml with basic project metadata and a .gitignore file, and
initializes a Git repository if not already present[realpython.com]. For example:
uv init myproject
will create a myproject folder with a skeleton project. You should run this once when starting a new
project.
uv add <packages>: Adds one or more packages to your project’s dependencies. This updates the
pyproject.toml with the package(s) (using the latest compatible version) and then resolves and
installs them, updating the uv.lock . You can specify versions or extras as needed. For instance:
uv add flask pandas
installs Flask and pandas and adds them to your dependencies. To add an optional dependency group (like a
dev group), use --group :
uv remove <packages>: Removes packages from your project. This will uninstall the package from the
environment and remove it from pyproject.toml . It will also update the lockfile to reflect that the
package is no longer needed. For example:
uv remove pandas
uv sync: Installs all dependencies for the project as per the uv.lock file. This is like a clean install – it
will ensure the environment exactly matches the locked versions. If you’re setting up a project for the first
time or want to reset your environment, uv sync is useful. It’s often used in CI pipelines. By default it
installs only the main dependencies; use uv sync --dev to include development dependencies (or --
group to specify a particular group)[forum.gitlab.com]. If you have a uv.lock , uv sync is faster
than a fresh resolve because it just installs from lock. If you don’t have a lock yet, uv sync will first run
a resolve and generate one.
uv lock: Resolves dependencies and updates the uv.lock file. You typically don’t need to run this
manually because uv add and uv remove automatically update the lock. However, you might use uv
lock to regenerate the lock (for example, after manually editing pyproject.toml or to update
dependency versions). You can use flags with uv lock to control the update:
uv lock --upgrade – Upgrade all dependencies to the latest versions allowed by the constraints in
pyproject.toml .
uv lock --check – Check if the lock is up to date with the current pyproject.toml (useful in
CI to ensure lock is not out of sync).
After running uv lock (with or without flags), you should run uv sync to actually install any new
versions from the updated lock.
uv run <command>: Runs a command in the context of the project’s virtual environment. This is
extremely handy – you can execute scripts, tests, or any Python command without activating the venv. For
example:
will run my_script.py using the project’s Python and installed packages. Or to run tests with pytest:
uv run will automatically create the virtual environment if it doesn’t exist and install any missing
dependencies. It essentially puts you in the environment temporarily for that command. If your project has
a script defined in pyproject.toml (under project.scripts ), you can also run it via uv run
<script-name> . This command is great for ensuring consistency – you know the command runs with
the exact versions in your lockfile.
uv build: Builds your project into distribution packages. By default, it creates both a source distribution
(tar.gz) and a wheel (whl) in a dist/ directory[docs.astral.sh]. For example:
uv build
uv publish: Uploads your built packages to a package index (like PyPI). You must have built the packages
first (with uv build ). For example:
uv publish
will upload the contents of the dist/ directory to PyPI. You’ll need to have your PyPI credentials
configured (typically via a .pypirc file or by logging in with twine beforehand). UV uses twine
under the hood for this, so it’s secure and handles API tokens, etc., appropriately. You can also specify a
repository URL if you’re using a private index. This command streamlines the release process – one
command to build and publish your package.
uv python install <version>: Installs a specific Python interpreter version for use with UV. For example:
will download and install Python 3.11 (the latest 3.11.x release) into UV’s Python directory. UV can
manage multiple Python versions, which is useful if you work on projects requiring different Python
versions. You can then use uv python list to see installed versions and uv python pin 3.11 to
set the default Python for the current project to 3.11[docs.astral.sh]. UV’s Python installation is separate
from your system Python – it uses standalone Python builds provided by the python-build-
standalone project[docs.astral.sh]. This means you don’t need to have Python pre-installed; UV can
fetch it for you.
uv venv <command>: This is a set of subcommands to manage virtual environments directly. For
example:
uv venv create – explicitly create a virtual environment (useful if you want to specify a Python
version or location).
uv venv activate – prints the command to activate the environment in your shell (useful if you
prefer to work in an activated venv).
Most of the time, you won’t need these because UV handles the environment automatically. But they’re
there for manual control. By default, UV creates .venv in the project root, which is standard and even
recognized by many IDEs.
uvx <package> [args]: uvx is a special command (short for uv tool run ) that lets you run a Python
package’s CLI entry point in an isolated environment, similar to pipx . For instance:
will download pycowsay (if not already cached), create a temporary environment, run the pycowsay
command with the given arguments, and then clean up. It’s great for running one-off tools without
installing them globally. You can also use uv tool install to permanently install a tool in a dedicated
environment (like pipx install )[github.com]. uvx is a convenient way to use Python CLI tools
without cluttering your system or main project.
uv self update: As mentioned earlier, this updates the UV tool itself to the latest version[realpython.com].
Good to run occasionally to get new features and fixes.
uv help: Shows general help, and you can do uv help <command> for help on a specific command.
UV’s help is quite detailed and worth checking if you forget a flag or option.
These commands cover most of the scenarios you’ll encounter in Python project management. To illustrate a
typical workflow:
4. Work on your code, and run scripts/tests via uv run (e.g. uv run pytest to run tests).
5. When ready to update a dependency, run uv lock --upgrade-package requests , then uv sync
to apply.
6. Before committing changes, ensure the lockfile is up to date (UV usually does this automatically, but you
can run uv lock --check in CI to verify).
7. To share or deploy, others can clone the project and run uv sync to get the exact environment.
8. When you’re ready to release, build and publish: uv build followed by uv publish .
This workflow is much more streamlined than doing all those steps manually with separate tools. UV’s design
is to make these common operations as simple as possible, while still allowing flexibility (for example, if you
need to use a specific version of a package, you can just edit pyproject.toml and run uv lock to adjust
the lockfile).
Performance Benchmarks
One of UV’s biggest selling points is its speed. While specific benchmarks can vary depending on the project
and system, numerous sources report that UV is orders of magnitude faster than traditional tools like pip
for many operations. Here we present some illustrative performance comparisons:
10–100× Faster Than pip: The UV documentation and developers often state a 10–100× speedup over
pip [github.com]. This is not an exaggeration in many cases. For example, installing a large project’s
dependencies that might take 5 minutes with pip could take 30 seconds or less with UV. This comes
from both faster resolution and faster download/install (thanks to parallelism and efficient caching). Even
for smaller tasks, the difference is noticeable – one user noted installing a single package with pip took
~1.6 seconds of CPU time, whereas UV took only ~0.28 seconds for the same package[medium.com].
UV’s efficient use of resources (low memory footprint and fast I/O) contributes to these
gains[dimasyotama.medium.com].
Comparison to Poetry: Poetry is generally faster than plain pip , especially with its newer resolver, but
UV has shown it can be faster still. In some tests, UV resolved dependencies in a fraction of the time
Poetry did for the same project. For instance, resolving a complex dependency graph that took Poetry 30
seconds might take UV only a few seconds. Anecdotal reports from users indicate that large projects that
were slow to update with Poetry now resolve “almost instantly” with UV[linkedin.com]. That said, the
actual speed difference can depend on the specific dependency set and Poetry version. Both are much faster
than the old pip resolver. The advantage of UV is more pronounced as projects grow in size or when using
features like multiple dependency groups.
CI Pipeline Speeds: In continuous integration (CI) environments, where fresh environments are built each
time, UV’s speed can significantly reduce pipeline duration. One data science team mentioned that their CI
environment setup went from over 25 minutes with Poetry down to just a few minutes after switching to
UV[fmind.medium.com]. Even smaller projects see benefits – installing a project’s dependencies in CI
might take 1–2 minutes with pip, but only 10–20 seconds with UV. This adds up when you have many CI
jobs or frequent commits. UV’s ability to cache packages globally also means subsequent runs can reuse
downloads, further cutting time. For example, UV’s cache can be persisted between CI jobs so that
packages aren’t re-downloaded on every run, similar to how you might cache node_modules in
JavaScript or vendor in Ruby.
Real-World Testimonials: Beyond numbers, many developers have shared their experiences. On Hacker
News and Reddit, users have posted side-by-side comparisons showing UV installing dozens of packages
in under a minute where pip took several. One user joked that switching to UV felt like “unlocking a cheat
code” for Python packaging due to how much faster their workflow became. While these are anecdotal,
they reinforce the notion that UV’s performance is a game-changer for those who work with large or
dependency-heavy Python projects.
It’s worth noting that UV’s speed isn’t just about raw time – it also improves the developer experience. Waiting
for dependencies is a common friction point; with UV, that waiting time is drastically reduced, making iterative
development more pleasant. Faster feedback loops in CI mean quicker test results and deployments. In short,
UV’s performance can lead to tangible productivity gains.
UV is a versatile tool suitable for a wide range of Python projects and use cases. Here are some scenarios
where UV shines, along with examples:
Web Development Projects: For web apps built with frameworks like Django or FastAPI, UV can manage
all the dependencies (the framework itself, ORMs, testing tools, etc.). Its speed is particularly beneficial
when setting up environments for development or running tests in CI. For example, a Django project with
many packages can be installed in a fraction of the time compared to using pip. UV’s lockfile ensures that
your production environment (perhaps in a Docker container) exactly matches what you tested locally. You
can even use UV within Docker to build your environment – there are guides showing how to create
efficient Docker images using uv sync to install only the necessary packages[autognosi.medium.com]
[bury-thomas.medium.com]. The workflow would be: define your web app’s dependencies in
pyproject.toml , use uv add to include things like flask or fastapi and any plugins, then uv
run python app.py to start your web server in the managed environment. This keeps your web project
clean and reproducible.
Data Science and Machine Learning: Data science projects often involve long lists of dependencies
(NumPy, pandas, scikit-learn, TensorFlow/PyTorch, Jupyter, etc.). Managing these with pip can be slow
and error-prone (especially if you have to reinstall frequently). UV can handle large dependency graphs
quickly, which is a big plus for data scientists who might spin up new environments often. It also ensures
that the versions of libraries like pandas or scikit-learn are consistent across different machines, which is
important for reproducibility of experiments. If you use Jupyter notebooks, you can even use UV to
manage the kernel environments. For example, you might create a UV project for a data analysis, add
pandas , matplotlib , scipy , etc., then run uv run jupyter notebook – UV will install
Jupyter if needed and launch it in the environment. Some data scientists have moved from Conda to UV for
their Python dependencies, citing faster environment creation and less bloat[datacamp.com]. Just keep in
mind that for packages outside PyPI (like specific CUDA versions), you might still need Conda, but UV
can manage the Python side.
DevOps and CI/CD Pipelines: UV is an excellent fit for CI/CD environments. Its speed means pipeline
steps for installing dependencies are much shorter. Moreover, because UV guarantees a clean install from
the lockfile, you can trust that your build or test environment in CI is identical to what developers have
locally. Many projects have started using UV in their GitHub Actions, GitLab CI, etc. For example, a
GitHub Actions workflow can include steps to install UV, then run uv sync to get all dependencies, then
run tests with uv run pytest . There are official setup actions for UV to make this easy[docs.astral.sh].
UV also provides Docker images that include UV and specific Python versions, which can be used as base
images for CI or deployment[docs.astral.sh]. Using UV in Docker can speed up image builds since
dependency installation is faster. Overall, UV reduces the variability and time in CI pipelines, leading to
quicker feedback and more reliable deployments.
CLI Tools and Scripts: If you develop Python command-line tools or scripts, UV can simplify distribution
and usage. You can manage your CLI tool’s dependencies with UV, and when it’s time to distribute, users
can install it via pip (if you publish to PyPI) or even run it directly with uvx if they have UV. Speaking
of uvx , it’s great for running one-off scripts or tools. For example, if you want to use a tool like
httpie to test an API, you can do uvx httpie GET https://api.example.com – UV will
handle fetching httpie and running it in an isolated environment, then you’re done. This is analogous to
npx in Node.js or cargo run in Rust, allowing on-demand execution of Python utilities without
manual installation. It’s especially handy for DevOps scripts or quick experiments where you don’t want to
clutter your global Python or create a full project.
Open Source Package Development: If you’re a developer of Python libraries (open source or internal),
UV can streamline your development and release process. You can use UV to manage test dependencies,
run tests, build your package, and publish it. For example, when working on a library, you might have a
pyproject.toml that lists your runtime dependencies and a [project.optional-dependencies]
section for test and dev tools. UV makes it easy to switch between different Python versions for testing
(using uv python install to get multiple interpreters and uv run --python 3.8 pytest to test
under a specific version). When you’re ready to release a new version of your library, uv build will
create the sdist/wheel, and uv publish will upload it to PyPI in one go[docs.astral.sh]. This reduces the
chance of mistakes in the release process. Many open-source projects have started adopting UV for these
reasons, often citing the convenience and speed improvements during development.
Educational and Personal Projects: For students or hobbyists, UV can simplify learning Python
packaging. Instead of teaching the complexities of virtual environments and requirements.txt , one
can teach uv init , uv add , and uv run . It lowers the barrier to doing things “the right way” from
the start. UV’s focus on pyproject.toml also aligns with modern practices, so learners get exposure to
standard tools. Additionally, UV’s self-contained nature means it’s easy to install on any machine (even
without Python pre-installed) and start working on a project immediately. This can be very helpful in
classroom settings or hackathons where environment setup can be a hurdle.
In all these cases, the common theme is that UV reduces friction. Whether you’re a data scientist trying to
quickly prototype with the right libraries, a web developer wanting a reliable deployable environment, or a
DevOps engineer aiming to optimize CI, UV offers a consistent and fast solution. Its growing adoption across
different domains (from web to ML to scripting) is a testament to its flexibility and usefulness.
While UV is a powerful tool, it’s important to be aware of its current limitations and some considerations
before fully adopting it:
New and Evolving Project: UV is relatively new (first released in 2024) compared to established tools
like pip or Poetry. This means it’s still evolving, and there might be bugs or missing features that are
present in more mature tools. The team is releasing updates frequently, but you should be prepared for the
possibility of encountering an edge-case issue or needing to update UV to get a fix for something. The flip
side is that the community and maintainers are very responsive – many issues get addressed quickly. Still,
in a critical production environment, some might prefer the battle-tested stability of older tools over a
newer one. That said, UV has reached a level of stability where many are using it in production
successfully, but it’s wise to keep an eye on its changelog and community discussions.
Smaller Ecosystem and Plugins: Because UV is new, it doesn’t yet have the extensive plugin or extension
ecosystem that, say, Poetry or pip have. Poetry has plugins for things like Docker integration, dependency
analysis, etc. UV is more minimalistic in that regard – it aims to have core features built-in. For most
needs, this is sufficient, but if you have a very specific workflow or tool that integrates with your package
manager, there might not be a UV plugin for it yet. You may need to use workaround scripts or request
features from the UV team. The good news is that UV’s design (using standard files and being CLI-driven)
means you can often achieve things with shell scripts or by directly using underlying tools (like using uv
run to execute a plugin script).
Learning Curve for Some Commands: UV’s CLI, while powerful, can be a bit different from what
you’re used to. For example, there isn’t a direct uv update command – you have to use uv lock --
upgrade or similar[loopwerk.io]. Some users have found the CLI structure a bit less intuitive than
Poetry’s for certain operations. It may take a little time to get used to which subcommand does what. The
official documentation is quite good, but it’s an extra thing to learn. If your team is used to pip or Poetry,
onboarding them to UV will involve teaching these new commands. That said, many find the learning
curve minor and well worth it for the benefits.
Dependency on Rust and Binary Releases: Since UV is a Rust binary, you’re dependent on the
maintainers to provide builds for your platform. Most common platforms (Windows, macOS, Linux
x86_64) are covered, and they even support Apple Silicon (arm64) which is great. But if you have an
uncommon architecture or need to compile UV yourself, you’ll need a Rust toolchain. For end-users this is
usually not an issue, but in environments where you can’t easily run external binaries or need to inspect the
source for security, UV being a closed-source (as in not interpreted) binary might be a consideration.
However, the source code is open on GitHub, so security-conscious users can review it or even build from
source if needed.
Handling of Certain Packages: While UV generally works with all Python packages, there might be some
edge cases. For instance, packages that have very complex installation logic or that rely on being installed
in a specific way (like some C extensions) should still work since UV ultimately uses pip under the hood
for installation. But there have been a few reports of issues with certain packages – for example, some GUI
libraries like PyQt5 had hiccups in early UV versions (due to how their dependencies are structured)
[github.com]. These kinds of issues tend to get fixed as the project matures (and often the fix is just a
matter of adjusting UV’s resolution logic or error handling). If you work with a lot of compiled packages or
older packages, it’s worth checking if there are any known issues. In most cases, people find UV handles
things just as well as pip does.
Not a Replacement for Conda for Non-Python Dependencies: As mentioned earlier, UV only manages
Python packages from PyPI. If your project requires system libraries, specific versions of non-Python
software, or you heavily use Conda’s package ecosystem (like for scientific libraries not on PyPI), UV
won’t replace Conda for those needs. You might end up using a combination: Conda to manage the base
environment with system-level packages, and UV to manage the Python packages on top. That’s a valid
approach, though it means you’re not fully moving away from Conda in that scenario. For pure Python
projects, this isn’t a concern, but it’s a consideration for data science or scientific computing projects.
Community and Support: While UV’s community is growing, it’s still smaller than that of pip or even
Poetry. If you run into a problem, finding help or examples might be a bit harder than with more
established tools. Stack Overflow questions about UV are starting to appear, but you might not find an
answer immediately. The official Discord or GitHub Discussions for UV are active and the maintainers are
helpful, so you can get support, but it’s a more direct channel than a broad community forum. This is
changing as adoption increases, but it’s something to note if you’re in an environment where you rely on
community support for tools.
In weighing these limitations, it’s important to consider your specific needs. For many, the benefits of UV far
outweigh these drawbacks. The development pace of UV is fast, so some of these limitations (like missing
features or rare bugs) are likely to be resolved as the tool matures. It’s also worth noting that UV is designed to
be compatible – if you ever need to fall back, you can still use pip commands via uv pip , or even
abandon UV and use the pyproject.toml with another tool. This flexibility means you’re not locked in.
One consideration often voiced is the sustainability of the project. UV is backed by a company (Astral), which
provides some assurance of ongoing development. The team also took over maintenance of the Rye project
(another Python packaging tool) to unify efforts[astral.sh], indicating a long-term commitment. Still, any
developer switching to a new tool should be comfortable that if the worst happens (the project is abandoned),
they can migrate their pyproject.toml and lockfile to another system. Given that pyproject.toml is
standard and UV’s lockfile format is documented, this migration path is relatively straightforward if needed.
In summary, UV is a robust and fast tool, but as with any new software, it’s wise to use it with awareness of its
current state. Test it in non-critical projects first if you’re unsure, and keep an eye on its development. Many
early adopters have been very happy with UV, and the trajectory suggests it will only improve.
Step 1: Install UV
If you haven’t already, install UV using the standalone installer or pipx as described earlier. Once installed,
verify it works by checking the version:
uv --version
You should see something like uv 0.6.x (or whatever the latest version is).
uv init uv-tutorial
UV will create a directory uv-tutorial and set up the initial project files. Change into that directory:
cd uv-tutorial
.python-version – a file indicating the Python version UV is using (by default, the latest Python
version).
Take a look at pyproject.toml . It should have entries under [project] for name , version ,
description , readme , requires-python , and dependencies . Initially, dependencies is empty.
uv add requests
UV will resolve the latest version of requests that is compatible with your Python version and add it to
pyproject.toml . You’ll see output showing that it creates a virtual environment (if not already there) and
installs requests along with any sub-dependencies. After it finishes, open pyproject.toml again – you
should see requests listed under dependencies with a version specifier (likely requests =
"^2.31.0" or whatever the latest is at the time). Also, a uv.lock file has been created, which contains the
exact resolved versions of requests and its dependencies (like urllib3 , certifi , etc.).
import requests
def main():
response = requests.get("https://api.example.com")
print(f"Status code: {response.status_code}")
print("Response:", response.text[:50] + "...") # Print first 50 chars
if __name__ == "__main__":
main()
This script makes a simple GET request to an example API and prints the response. Now, run the script using
UV:
uv run main.py
UV will execute main.py in the project’s virtual environment. It should output something like:
(depending on what the example API returns). The first time you run uv run , UV ensures all dependencies
are installed and creates the virtual environment. You’ll notice that you didn’t have to manually activate the
environment or install anything else – UV handles it. If you check the directory, there’s now a .venv folder
containing the virtual environment, and uv.lock is fully populated.
This will output something like source .venv/bin/activate (on Linux/macOS) or the appropriate
command for Windows. Run that command to activate the environment. Now you can check that requests
is installed:
pip list
You should see requests and its dependencies in the list. You can even run python main.py directly
now since the environment is active. When you’re done, deactivate the environment with deactivate (or
close the terminal).
This adds pytest to an optional dependency group named “dev” in pyproject.toml . Now, if we run uv
sync normally, pytest won’t be installed (since it’s optional). But we can install it by specifying the group:
Now pytest and its dependencies are installed. You can verify by running uv run pytest --version .
This is a good way to separate testing/linting tools from your main project dependencies.
import main
def test_main():
# This is a dummy test; you might want to mock requests in a real test
assert True
Then run:
uv run pytest
UV will run pytest and discover the test. It should pass. This demonstrates how easy it is to run project
commands with UV.
uv build
This will create a source distribution and wheel in dist/ . If you have a PyPI account and wanted to publish,
you could do uv publish (you’d need to configure credentials first). For this tutorial, building is enough to
see that UV can handle packaging as well.
uv venv remove
This deletes the .venv directory. The pyproject.toml and uv.lock remain, so you can always
recreate the environment with uv sync later.
By following this tutorial, you’ve experienced the end-to-end workflow with UV: initializing a project, adding
dependencies, running code, and even building the project. Notice how everything was handled with a small set
of commands and no manual environment juggling. This simplicity is what makes UV appealing for many
developers.
Conclusion
UV represents a significant step forward in Python package management. It combines the functionality of
multiple tools into one fast, user-friendly interface, tackling many of the pain points that Python developers
have faced with traditional workflows. By leveraging a modern resolver and efficient implementation, UV
dramatically speeds up dependency resolution and installation, which can greatly improve productivity and CI
performance. Its use of standard Python packaging files ( pyproject.toml and a lockfile) ensures
compatibility and reproducibility, aligning with Python’s evolving best practices.
For those willing to try a new tool, UV can streamline your development process – from the moment you start
a project to the moment you deploy it. Many who have adopted UV report that it “just works” and integrates
seamlessly into their existing projects, whether they’re working on web applications, data science scripts, or
libraries. The fact that it can replace so many other tools (venv, pip, pip-tools, pyenv, twine, etc.) means less
cognitive overhead and fewer moving parts to manage.
Of course, as with any new tool, it’s important to consider your team’s needs and comfort level. If you work in
an environment where everyone is already happy with Poetry or pip, introducing UV would require some
onboarding. However, given UV’s design, the learning curve is not steep and the potential benefits are large.
It’s also reassuring that you can use UV’s pip compatibility layer or even fall back to other tools if needed,
so you’re not making an all-or-nothing bet.
In the Python packaging landscape, UV is a strong contender that brings much-needed innovation. Its
continued development and the backing of a dedicated team suggest that it will only grow more feature-rich
and stable. As one user put it after a year of using UV in various projects, “the cost of moving to and from it is
low, but the value it delivers is quite high”[bitecode.dev]. This encapsulates the sentiment of many: trying UV
is worth it, and in most cases, you’ll wonder why you didn’t switch sooner.
Whether you’re starting a new project or looking to modernize an old one, UV provides a robust, fast, and
comprehensive solution for managing Python packages and environments. It’s an exciting time for Python
packaging, and UV is at the forefront of making dependency management fast, easy, and reliable for all kinds
of Python developers.