Project Setup

uv tool install vs pipx for CLIs

Compare uv tool install and pipx for installing Python CLIs globally: isolation, speed, upgrades, and when uvx or pipx run fits a one-off tool.

Updated

uv tool install and pipx install solve the same core problem: they put a Python command-line tool on your PATH without letting its dependencies collide with anything else on your machine. Each tool gets its own isolated virtual environment, and you type its name like any other program. The differences show up in speed, upgrade ergonomics, and how you run something just once. This guide puts the two side by side for the specific job of installing and living with globally available CLIs.

TL;DR

  • Both give isolated global installs. Each installed CLI lands in a dedicated virtual environment, so httpie and awscli never fight over requests versions.
  • uv tool install is faster thanks to uv's Rust resolver and a shared global cache — repeat installs reuse already-downloaded wheels.
  • uvx and pipx run are the one-off runners. Use them to execute a tool once without leaving anything installed.
  • Upgrades differ: uv tool upgrade re-resolves against your constraints; pipx upgrade does the same for pipx-managed venvs. Both let you pin.
  • Pick uv if you already use it for project work (one tool, one cache); pick pipx if you want a small, single-purpose installer that does nothing else.

Both give you isolated global installs

The shared idea is worth stating plainly because it is the whole reason these tools exist. If you pip install httpie into your system Python, its pinned requests and pygments become global, and the next tool that wants a different pygments breaks. Isolated installers avoid this by creating one virtual environment per tool and exposing only that tool's entry-point scripts on your PATH.

# pipx: one venv for black, another for httpie
$ pipx install black
$ pipx install httpie

# uv: same model, different front door
$ uv tool install black
$ uv tool install httpie

After either pair of commands, black and http are on your PATH, backed by separate environments that can hold conflicting dependency trees without noticing each other. This is the same isolation principle covered in managing virtual environments for cross-platform CLIs — an installed tool is just a managed, named venv you never have to activate.

uv tool install / uvx vs pipx install / pipx run

Both tools split the world into two verbs: install and keep versus run once.

# Keep it around (adds a command to your PATH)
$ pipx install cowsay
$ uv tool install cowsay

# Run it once, throw the environment away afterwards
$ pipx run cowsay "hello"
$ uvx cowsay "hello"

uvx is simply an alias for uv tool run. It resolves the requested package, builds (or reuses a cached) ephemeral environment, runs the command, and leaves nothing on your PATH. pipx run behaves the same way and caches the temporary environment for a few days so a second pipx run of the same tool is quick.

The one-off runners are the right call for tools you touch rarely: a project scaffolder, a linter you only run in CI, a cookiecutter template. There is no reason to permanently install something you invoke twice a year — reach for uvx or pipx run and skip the cleanup. The scaffolding workflow in CLI project scaffolding with Cookiecutter is a textbook case: uvx cookiecutter gh:org/template runs the generator without adding a command you will never use again.

Speed and the shared cache

This is where uv pulls ahead. uv keeps a single global cache of downloaded and built wheels and resolves dependencies with a parallel Rust resolver. When you install a second tool that shares a dependency with the first, uv links the already-cached wheel instead of re-downloading it. Cold installs are fast; warm installs are close to instant.

# First install populates the cache
$ uv tool install rich-cli

# A later install that shares 'rich' reuses cached wheels
$ uv tool install textual

pipx is a thin, pure-Python layer over venv and pip. It is perfectly fast enough for interactive use, but it does not share a wheel cache across tool environments the way uv does, so each install does more independent work. If you install and reinstall many tools — for example, rebuilding a toolbox in a fresh container — the difference is noticeable.

Upgrades and pinning

Both tools let you upgrade one CLI, upgrade everything, or pin to an exact version.

# Upgrade a single tool
$ pipx upgrade httpie
$ uv tool upgrade httpie

# Upgrade every installed tool
$ pipx upgrade-all
$ uv tool upgrade --all

# Pin at install time
$ pipx install 'httpie==3.2.2'
$ uv tool install 'httpie==3.2.2'

An upgrade re-resolves the tool's dependencies against its version constraints and rebuilds the environment. Pinning matters for reproducibility: if a CLI is part of your team's workflow, pin the version in your setup docs so everyone runs the same one. You can also inject extra packages into a tool's environment — useful when a CLI needs an optional plugin:

# Add a plugin into an existing tool environment
$ pipx inject mkdocs mkdocs-material
$ uv tool install mkdocs --with mkdocs-material

Note the small ergonomic difference: pipx injects into an already-installed tool as a separate step, while uv declares extras with --with at install time (re-running the install updates the set). Both end with the plugin importable inside the tool's isolated environment.

uv tool list and managing what's installed

Listing and removing work the same way in both, which makes auditing your global toolbox easy.

# What is installed, and which commands does each expose?
$ uv tool list
black v24.10.0
- black
- blackd
httpie v3.2.4
- http
- https

$ pipx list

# Remove a tool and its environment
$ uv tool uninstall black
$ pipx uninstall black

uv tool list shows both the package version and every console script that package puts on your PATH, which is handy when a single distribution installs several commands (like black and blackd). One more uv-specific nicety: uv tool update-shell ensures the tool bin directory is on your PATH, the equivalent of pipx's pipx ensurepath.

Comparison table

Dimensionuv toolpipx
Isolation modelOne venv per toolOne venv per tool
Install a tooluv tool install Xpipx install X
Run once, no installuvx X / uv tool run Xpipx run X
Upgrade one / alluv tool upgrade X / --allpipx upgrade X / upgrade-all
Add a plugin--with pkg at installpipx inject tool pkg
List installeduv tool listpipx list
Shared wheel cacheYes (global)No
ResolverRust, parallelpip (pure Python)
Scope of the toolFull project + package managerInstall/run CLIs only
Runtime dependencySingle static uv binaryPython + pip

When each wins

Reach for uv tool when you already use uv for project dependency management. Keeping one binary, one cache, and one mental model for both your projects and your global tools is a real simplification — the same uv you use in uv for Python CLI dependency management also installs finished CLIs. It is also the fastest option for cold environments like CI runners and Docker images.

Reach for pipx when you want a small, focused, well-established tool that does exactly one thing. pipx has been the community standard for global CLI installs for years, it is packaged in most distributions, and it carries no expectation that you adopt a whole project workflow. If uv is not already in your stack, pipx is the lower-commitment choice. The end-to-end distribution story — building the tool and telling users to install it with pipx — lives in installing and distributing CLIs with pipx.

For most teams the honest answer is "whichever you already have." Both deliver the same isolation guarantee; the deciding factor is how much other tooling you want to standardize on.

Production notes

  • Publishing advice should name a runnable installer. When you document install steps for your CLI, offer both pipx install yourcli and uv tool install yourcli — users have one or the other, rarely both.
  • CI: prefer the one-off runner. In CI you usually want uvx ruff check or pipx run ruff check rather than a persistent install; the ephemeral environment is cached and there is no global state to clean up.
  • PATH setup is a first-run gotcha. Both tools install scripts into a bin directory that may not be on a fresh machine's PATH. Run uv tool update-shell or pipx ensurepath once and restart the shell.
  • Pin tools your team depends on. An unpinned uv tool install/pipx install floats to the latest release; pin the version in onboarding docs to avoid "works on my laptop" drift.
  • Python version. uv tool install --python 3.12 X lets uv fetch and use a specific interpreter for the tool's environment; pipx relies on a Python already present on the system.