python

Switching from Pip to uv in Python: A Comprehensive Guide

Damilola Olatunji

Damilola Olatunji on

Switching from Pip to uv in Python: A Comprehensive Guide

Python has long relied on pip as its standard package manager, but a blazing-fast alternative is now changing the landscape.

uv is a Rust-based package manager that aims to transform Python dependency management with unmatched performance and simplicity.

In this guide, you will learn everything you need to know about uv and how to smoothly transition from the traditional pip ecosystem.

Let's get started!

What is uv?

uv is a high-performance Python package and project manager written in Rust. It was developed by Astral (the company behind the popular Ruff linter), and is designed to be the comprehensive solution for Python package management.

With uv, you can replace a wide range of traditional tools in your workflow, such as:

  • pip for installing packages.
  • pip-tools for dependency locking.
  • virtualenv/venv for environment management.
  • pyenv for managing Python versions.
  • pipx for installing CLI tools.
  • poetry for project configuration and publishing.
  • twine for distributing packages.

Thanks to its Rust-based roots, uv performs operations 10-100 times faster than these traditional Python tools, and this increase in speed is noticeable even in small projects.

Benchamrking uv against standard Python tools

uv also embraces modern packaging standards. It uses pyproject.toml for configuration and introduces a reliable lock file that ensures consistent environments across systems.

Before we explore uv in detail, let's quickly recap the current Python landscape for package management.

Traditional Python Package Approach: pip and virtualenv

The traditional Python package management workflow relies on a fragmented set of tools, each handling a specific piece of the puzzle.

It typically begins with installing Python, either via system package managers or by downloading it from the official website.

From there, you might use tools like venv or virtualenv to create isolated environments, followed by pip to install packages. Dependency tracking is often manual or handled through add-ons like pip-tools.

A typical workflow with pip and virtualenv might look like this:

Shell
# Create a virtual environment python -m venv .venv # Activate the environment source .venv/bin/activate # Install packages pip install django requests # Record dependencies pip freeze > requirements.txt

While this workflow is well-known and widely used, it involves multiple steps and tools, each with its own syntax and quirks. You must remember to activate environments, manage dependencies manually, and juggle a variety of commands.

This is precisely the pain point uv aims to solve by streamlining the entire process into a faster and more cohesive experience.

Installing uv

Getting started with uv is quick and straightforward, with support across all major operating systems. You can follow the official uv installation guide, or use one of the commands below.

On Linux and macOS, run:

Shell
curl -LsSf https://astral.sh/uv/install.sh | sh

On Windows, use PowerShell:

Shell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

After installation, confirm that it's working by checking the version:

Shell
uv --version
Shell
uv 0.7.3

To update uv later, simply run:

Shell
uv self update

This will fetch and install the latest version automatically:

Shell
info: Checking for updates... success: Upgraded uv from v0.6.13 to v0.7.3! https://github.com/astral-sh/uv/releases/tag/0.7.3

The easiest way to begin using uv is to replace the tools you already rely on. Since uv is designed as a drop-in replacement, you can take advantage of its performance and simplicity without overhauling your current workflow.

Let's look at how that works in practice.

Managing Virtual Environments with uv for Python

uv can serve as a direct replacement for tools like virtualenv and venv to create isolated Python environments.

Previously, you might run:

Shell
python -m venv .venv

With uv, the equivalent is:

Shell
uv venv # uses .venv by default

By default, this creates a virtual environment in .venv, but you can specify a custom path if needed:

Shell
uv venv <path/to/environment>

After running the command, uv provides a clear summary showing which Python interpreter was used, where the environment was created, and how to activate it in your shell:

Shell
Using CPython 3.13.2 interpreter at: /home/ayo/.local/share/mise/installs/python/3.13.2/bin/python Creating virtual environment at: .venv Activate with: source .venv/bin/activate.fish

You can also require that a specific version of Python is used:

Shell
uv venv --python 3.11

If the version isn't already installed, uv will fetch and install it for you automatically.

In practice, uv's environment creation is incredibly fast. In my tests, it was roughly 200x faster than venv:

Creating virtual environments with uv is 200x faster than venv

Replacing Pip with uv for Dependency Management

uv provides a pip-compatible interface for managing your project dependencies. You only need to prefix specific pip commands with uv. For example, you can install dependencies with:

Shell
uv pip install flask requests
Installing packages with Pip

There's no need to activate a virtual environment beforehand, as uv automatically detects and uses the nearest environment in your current or parent directory.

If no environment is found, it will prompt you to create one or suggest using the --system flag to install it globally:

uv pip install fails without a virtual environment

Installing dependencies from existing project files works the same way:

Shell
uv pip install -r requirements.txt
Shell
uv pip install -r pyproject.toml

If you're using pip-tools to compile requirements.txt from a requirements.in file, uv provides a much faster alternative:

Shell
pip-compile requirements.in -o requirements.txt

With the uv equivalent:

Shell
uv pip compile requirements.in -o requirements.txt

I experienced a ~150x speedup over pip-compile:

uv compile is faster than pip-compile

Removing dependencies is also straightforward with:

Shell
uv pip uninstall flask

Managing Python Versions with uv

One of uv's key advantages is that it doesn't rely on Python to run. Instead, it automatically detects existing Python installations and uses them for any Python-specific tasks.

If a project requires a version of Python that isn't already installed, uv will also download and manage it for you.

You can install Python versions directly like this:

Shell
uv python install # the latest version uv python install 3.13 # a specific version uv python install 3.13 3.12 # multiple versions

To check which versions are available on your system, run:

Shell
uv python list --only-installed

This lists all Python versions installed by uv, as well as any others already on your system, regardless of how they were installed:

Shell
cpython-3.13.2-linux-x86_64-gnu /home/ayo/.local/share/mise/installs/python/3.13.2/bin/python3.13 cpython-3.13.2-linux-x86_64-gnu /home/ayo/.local/share/mise/installs/python/3.13.2/bin/python3 -> python3.13 cpython-3.13.2-linux-x86_64-gnu /home/ayo/.local/share/mise/installs/python/3.13.2/bin/python -> python3.13 cpython-3.12.10-linux-x86_64-gnu /home/ayo/.local/share/uv/python/cpython-3.12.10-linux-x86_64-gnu/bin/python3.12 cpython-3.12.3-linux-x86_64-gnu /usr/bin/python3.12 cpython-3.12.3-linux-x86_64-gnu /usr/bin/python3 -> python3.12 cpython-3.11.12-linux-x86_64-gnu /home/ayo/.local/share/uv/python/cpython-3.11.12-linux-x86_64-gnu/bin/python3.11

With uv, you don't need to install Python ahead of time. If a command requires a version you don't yet have, uv will fetch and install it automatically before proceeding. This on-demand behavior eliminates the need for separate tools like pyenv.

Simplifying Tasks with uv

So far, I've focused on how uv can serve as a faster, drop-in replacement for tools you're already using, allowing you to gain performance benefits with minimal disruption.

But uv also introduces an alternative, more modern workflow that simplifies many tasks and goes beyond what traditional tools offer.

In the next sections, we'll explore some of these new capabilities.

Creating and Managing Python Projects with uv

One of uv's core goals is to manage the entire lifecycle of a Python project, from creating the project itself, to managing its dependencies, running commands or scripts, and even preparing it for distribution.

To start a new project, run:

Shell
uv init <project>

By default, this sets up an application-style project. To create a library project instead, use:

Shell
uv init --lib <project>

An initialized application project has the following structure:

text
. ├── .git ├── .gitignore ├── main.py ├── pyproject.toml ├── .python-version └── README.md

uv sets up version control with Git, includes pyproject.toml for project metadata and dependencies, and creates a basic script (main.py) along with a .python-version file. It's ready to run out of the box.

Execute the script with:

Shell
uv run main.py

The first time you run this, uv will select the appropriate Python version, create a virtual environment, and then execute the script:

Shell
Using CPython 3.12.10 Creating virtual environment at: .venv Hello from example-project

At this point, uv generates a uv.lock file, which records the full, resolved dependency graph — ensuring consistent environments across different machines.

While pyproject.toml defines intended dependencies (like requirements.in), uv.lock captures the exact installed versions, effectively replacing requirements.txt for reproducibility.

Managing Dependencies to a uv Project

It's simple to add dependencies to a uv project. To add a package, use:

Shell
uv add requests
Installing dependencies with uv

For development dependencies or tools, append the --dev flag:

Shell
uv add --dev black

These changes are reflected in your pyproject.toml:

TOML
# pyproject.toml . . . dependencies = [ "requests>=2.32.3", ] [dependency-groups] dev = [ "black>=25.1.0", ]

You can create custom dependency groups using the --group flag:

Shell
uv add --group tools black uv add --group production gunicorn uv add --group dev pytest # same as uv add --dev

Resulting in:

TOML
[dependency-groups] dev = [ "pytest>=8.3.5", ] production = [ "gunicorn>=23.0.0", ] tools = [ "black>=25.1.0", ]

This structure makes it easy to install only the dependencies needed for a given environment. For example, to install just the base and production dependencies, run:

Shell
uv sync --no-group dev --no-group tools --group prod

Dependencies are always installed into the project's virtual environment, so there's no need to activate it manually. Once installed, you can run any tool using:

Shell
uv run <tool>

As in:

Shell
uv run pytest

This finds the tool version in your project's virtual environment and executes it:

uv run pytest

uv also provides a tools interface for working with tools that aren't tied to a specific project:

Shell
uv tool run <tool> # Same as uvx <tool>

In this case, the tool and its dependencies are installed in a temporary virtual environment and executed accordingly. If you use the tool regularly, you can install it for easier access:

Shell
uv tool install <tool>

Upgrading and Removing Dependencies

uv provides straightforward commands for updating and removing packages as your project evolves. Here's how to upgrade a specific package to its latest version:

Shell
uv add --upgrade requests

This command updates the package and refreshes the lock file to ensure consistent environments across your team.

Removing packages is equally simple:

Shell
uv remove requests

uv will clean up the pyproject.toml, remove unneeded transitive dependencies, and regenerate the uv.lock file automatically.

Migration Guide: From Pip to uv

Switching an existing project from Pip to uv is quite straightforward. Below is a quick reference table showing how common pip commands map to their uv equivalents:

Pip Commanduv EquivalentDescription
python -m venv .venvuv venvCreates a virtual environment
pip install packageuv add packageInstalls a package and updates project files
pip install -r requirements.txtuv pip install -r requirements.txtInstalls from requirements file
pip freeze > requirements.txtuv export -o requirements.txtExports dependencies
pip listuv pip listLists installed packages
pip uninstall packageuv remove packageRemoves a package

If your project currently uses pip and requirements.txt, you can migrate it to uv in just a few steps.

  1. Initialize uv in the existing project directory:

    Shell
    uv init .

    This creates a pyproject.toml file and sets up the project structure, without replacing any existing files.

  2. Remove the old virtual environment:

    Shell
    rm -rf .venv
  3. Install dependencies from requirements.txt:

    Shell
    uv add -r requirements.txt

    This installs the dependencies listed in your requirements.txt file in a virtual environment, and updates the pyproject.yml and uv.lock files accordingly.

  4. Create a new uv-managed environment:

    Shell
    uv sync
  5. Verify that everything works — for example (assuming you have a Django project):

    Shell
    uv run manage.py migrate
    Shell
    uv run manage.py runserver
Using uv in a Django Project

Now your project should be fully migrated to uv with the same functionality as before, but now with uv's performance advantages.

Wrapping Up

By unifying the roles of multiple tools into one high-performance system, uv eliminates much of the friction developers have long dealt with.

Its speed alone is a compelling reason to adopt it, but uv's streamlined workflow and modern packaging standards make it even more appealing. For most projects, the transition is simple and the productivity gains are immediate.

uv's development is a strong signal of where the broader Python ecosystem is headed. Now is an ideal time to adopt this tool built for the future.

Thanks for reading!

Wondering what you can do next?

Finished this article? Here are a few more things you can do:

  • Share this article on social media
Damilola Olatunji

Damilola Olatunji

Damilola is a freelance technical writer and software developer based in Lagos, Nigeria. He specializes in JavaScript and Node.js, and aims to deliver concise and practical articles for developers. When not writing or coding, he enjoys reading, playing games, and traveling.

All articles by Damilola Olatunji

Become our next author!

Find out more

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps