Skip to content

uvx vs uv: When to Use Each Tool in Python Development

I kept typing uv run ruff check . when I should have used uvx ruff check .. Then I’d wonder why my project’s virtual environment got cluttered with tools I only needed once. The confusion between uv and uvx slowed me down for weeks.

If you’re scratching your head about when to use which command, you’re not alone. Both tools come from the same ecosystem, but they solve different problems. Let me clear this up.

What is uv?

uv is the main package and project manager from Astral. Think of it as pip, poetry, pyenv, and pipx rolled into one fast tool.

When I use uv, I’m working with my project:

  • Managing dependencies with uv add and uv remove
  • Creating and syncing virtual environments with uv sync
  • Running commands that need my project installed with uv run
  • Installing Python versions with uv python install
  • Creating new projects with uv init

The key insight: uv operates within your project context. It knows about your pyproject.toml, your virtual environment, and your installed packages.

project-management.sh
# Initialize a new project
uv init myproject
# Add a dependency
uv add requests
# Run a script that uses your project's packages
uv run python main.py

What is uvx?

uvx is a convenience alias for uv tool run. It runs Python tools in isolated, temporary environments.

When I use uvx, I’m saying: “Run this tool once, don’t mess with my project.”

The tool runs in its own clean environment, does its job, and leaves no trace in my project. This is perfect for:

  • Tools I don’t want installed permanently
  • Testing different versions of tools
  • Quick one-off commands
isolated-tool-runs.sh
# Run ruff without installing it anywhere
uvx ruff check .
# Try a specific version of black
# Run Python itself in isolation
uvx python -c "print('hello')"

Key Differences at a Glance

Aspectuvuvx
PurposeProject managementOne-off tool runs
EnvironmentUses project venvCreates temporary venv
PersistenceChanges persistEphemeral, cleaned up
Use caseDependencies, scriptsTools you don’t need installed
SpeedFast (uses existing venv)Fast (creates temp venv on demand)

Decision Framework

When I reach for a command, I ask myself these questions:

1. Does the tool need access to my project’s installed packages?

If yes, use uv run. For example, pytest needs to import my project’s code. mypy needs to check my project’s types.

project-aware-runs.sh
# pytest needs your project installed
uv run pytest tests/
# mypy needs access to your source code types
uv run mypy src/
# Your project's CLI tool
uv run myproject-cli --help

If no, consider uvx. For example, ruff just needs to read files. It doesn’t need my project’s packages.

isolated-runs.sh
# ruff only needs to read files
uvx ruff check .
# black only needs to format files
uvx black .
# Run any tool without project context
uvx cowsay "Hello, isolated world"

2. Is this a one-off command I won’t repeat?

If I’m testing a tool or running it once, uvx is the answer. No need to pollute my project or global tools.

one-off-commands.sh
# Test a linter I'm considering
uvx ruff check .
# Run a tool for a quick check
uvx mypy --version
# Try a different version
uvx [email protected] --version

3. Will I use this tool frequently across projects?

If yes, install it as a tool with uv tool install. This installs it globally (but in its own environment).

frequent-tools.sh
# Install ruff globally for frequent use
uv tool install ruff
# Now run it directly
ruff check .
# Install specific version
uv tool install [email protected]

4. Am I managing dependencies?

For adding, removing, or syncing packages, use uv commands directly:

dependency-management.sh
# Add a dependency
uv add requests
# Add a dev dependency
uv add --dev pytest
# Remove a dependency
uv remove requests
# Sync environment with lock file
uv sync

Real-World Scenarios

Scenario 1: Running Linters

I want to check my code with ruff. Does ruff need my project installed? No. It just reads files and checks them.

linter-scenario.sh
# Good: uvx for isolated run
uvx ruff check .
# Also fine if I use ruff a lot:
uv tool install ruff
ruff check .
# Overkill: uv run (creates unnecessary project context)
uv run ruff check . # Works but unnecessary

Scenario 2: Running Tests

I want to run my test suite. Does pytest need my project installed? Yes. It imports my code to test it.

test-scenario.sh
# Correct: uv run for project context
uv run pytest tests/
# Wrong: uvx won't have your project installed
uvx pytest tests/ # Fails - can't find your code

Scenario 3: Testing a New Tool

I heard about a new formatter and want to try it on my project.

testing-tool.sh
# Perfect: uvx for one-off testing
uvx ruff format . # Try it without committing
# If I like it, install for frequent use
uv tool install ruff
ruff format .

Scenario 4: CI/CD Pipeline

In CI, I want clean, reproducible runs.

ci-pipeline.sh
# Use uvx for tools that don't need project context
uvx ruff check .
uvx ruff format --check .
# Use uv run for tests
uv run pytest
uv run mypy src/

Common Mistakes

Mistake 1: Using uv run for everything

I used to run uv run ruff check . constantly. This created unnecessary project context. Now I use uvx ruff check . for tools that don’t need my project.

Mistake 2: Installing tools I use once

I installed tools globally with uv tool install just to try them. My global tool list became a mess. Now I use uvx for testing.

Mistake 3: Using uvx for project tools

I tried uvx pytest and wondered why my tests couldn’t import my code. The isolated environment didn’t have my project installed. uv run pytest was the answer.

Quick Reference

quick-reference.sh
# Project management
uv init [name] # Create new project
uv add [package] # Add dependency
uv remove [package] # Remove dependency
uv sync # Sync with lock file
# Running in project context
uv run [command] # Run with project installed
uv run pytest # Tests need your code
# Isolated tool runs
uvx [tool] # Run once, clean environment
uvx ruff check . # Linter doesn't need project
uvx python -c "..." # Isolated Python
# Frequent tool use
uv tool install [tool] # Install globally
ruff check . # Then run directly

Summary

In this post, I explained when to use uvx versus uv in Python development. The core principle is simple: uvx for isolated tool runs that don’t need your project, uv run for commands that need your project installed. Think of uvx as “run this tool somewhere clean” and uv run as “run this command in my project.”

The decision comes down to one question: does the tool need to import my project’s code? If yes, uv run. If no, uvx. For tools you use daily across projects, install them with uv tool install.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments