How do uv and conda compare?
uv and conda both create isolated environments and install packages, but they draw from different worlds. uv installs Python packages from PyPI, the Python Package Index. conda installs from its own repositories, called channels, and what it ships there is not limited to Python: one conda package can carry a C library or the R language. That single difference, where the packages come from, drives every trade-off below.
Which should you use?
uv is the better default for most Python projects. When your dependencies are on PyPI as wheels, the prebuilt archive format Python packages ship in, uv installs them faster, records them in pyproject.toml, the standard project file other Python tools can also read, and manages Python versions without a second tool.
If uv is not on your machine yet, How to install uv is one command, and How to install Python with uv covers interpreters after that.
If you already have Anaconda installed, nothing here asks you to remove it. uv installs alongside it and the two coexist, with one caveat about your Python version covered below.
Reach for the conda ecosystem when you need native libraries, meaning compiled C, C++, or Fortran code that is not itself a Python package and that PyPI has no way to deliver: the full GDAL stack with its command-line tools, a CUDA toolkit resolved against matching packages, or compilers and non-Python languages sharing one environment.
Most people arrive here holding one of these options:
| Option | Choose it when |
|---|---|
| Anaconda, already installed | It works and you have no licensing pressure. Keep it, and add uv per project as you go. |
| uv | Every dependency is a PyPI wheel. Fastest, standard pyproject.toml, one program. |
| Miniforge + conda | You need conda-forge’s native libraries and your team already runs conda. Free for all use. |
| pixi | You need conda-forge packages but want project-local environments and one lockfile. |
| Paid Anaconda license | Your organization is above the employee threshold and wants Anaconda’s curated channel or support contract. |
Longevity cuts against uv here. conda has been in production since 2012, and research labs and enterprises have years of tooling built around it; uv is pre-1.0 and much younger. Can you trust uv long term? weighs that risk, and Why should I choose conda? argues the other side.
If you are adopting conda today for the native-library reason, evaluate pixi first. pixi installs the same conda-forge packages, adds project-local environments and a single lockfile spanning conda and PyPI dependencies, and carries no Anaconda licensing obligation. When should I choose pixi over uv? covers that decision in depth.
Side-by-side comparison
| Consideration | uv | conda |
|---|---|---|
| Package source | PyPI (wheels and source distributions) | conda channels (conda-forge, Anaconda default) |
| Ships the Python interpreter | Yes (python-build-standalone) | Yes (as a conda package) |
| Native / non-Python libraries | Only via self-contained wheels | First-class |
| GDAL CLI tools, CUDA toolkit | Not available | First-class |
| Environment creation (4-package scientific stack, cold) | ~4s | ~40s |
| Lockfile | Built in, cross-platform (uv.lock) |
Same-platform via conda list --explicit; cross-platform needs conda-lock |
| Config format | pyproject.toml |
environment.yml |
| Environment location | Project-local .venv |
Named central envs, or project-local with -p ./env |
| Licensing | Free (PyPI) | Tool free; Anaconda default channel commercial above 200 employees |
| Best fit | Pure-Python and PyPI projects | Cross-language, native-heavy scientific stacks |
Where your packages come from
uv pulls from PyPI: wheels, plus source distributions for the packages that publish no wheel. For the interpreter itself, uv downloads a prebuilt Python (from the python-build-standalone project) rather than compiling one, so uv python install 3.13 works on a machine with no compiler. Everything uv installs is a Python package.
conda pulls from its channels: the community-maintained conda-forge or Anaconda’s default channel. A conda package is not limited to Python. One can carry a C library, a Fortran runtime, a command-line tool, or an R module. That range exists because conda was built for scientific computing, where a Python package is often a thin wrapper around a C or Fortran library that has to be installed too. conda ships and manages its own Python interpreter as just another conda package.
That difference decides what each tool can install. If a dependency exists as a wheel on PyPI, uv installs it. If it needs a system library that PyPI cannot ship, conda’s channels usually carry it.
conda also solves a problem uv never has to: keeping every compiled library in an environment binary-compatible with the others. That is why conda’s solver, the component that works out which versions of which packages can be installed together, has more to do than uv’s.
What does uv replace in an Anaconda install?
Anaconda Distribution is a bundle rather than a tool. One installer delivers conda, a Python interpreter, over 600 preinstalled packages, and Anaconda Navigator, a desktop GUI for creating environments and launching JupyterLab or Spyder. uv is one command-line program, which makes this a comparison between a whole distribution and a single tool.
uv replaces two of those four parts. It takes over conda’s job (uv add installs packages, uv sync builds the environment) and the interpreter’s (uv python install). The preinstalled bundle and Navigator have no counterpart.
Losing the bundle is the design rather than a gap. Nothing is installed in a uv project until you add it to pyproject.toml, so the dependency list you wrote is the environment, rather than a fraction of a 5 GB installation you never chose. Losing Navigator is a real cost if you use it: uv is command-line only, and a workflow built on launching notebooks from Navigator becomes one that launches them from the project. How to run a Jupyter notebook with uv covers the swap.
Which installer you have also decides whose licensing terms apply, since Miniconda carries Anaconda’s channel while Miniforge does not. Understanding the conda and Anaconda ecosystem maps which installer defaults to which channel.
What about licensing?
The conda tool is free, BSD-licensed open source, and conda-forge is free for all use including commercial. What triggers a bill is Anaconda’s default channel at repo.anaconda.com: any organization with more than 200 employees needs a paid license to use it, and running conda install against that channel counts even from a free Miniconda install. Automated use counts too, so CI runners and Docker images built on Anaconda’s channel fall under the same terms.
Switching to Miniforge, which defaults to conda-forge, avoids the charge entirely and keeps every conda workflow intact.
uv carries no comparable terms. PyPI and uv are free for any use.
Note
Anaconda has revised these terms more than once, and edge cases (contractors in the headcount, academic and nonprofit status, which channels are covered) are not settled by the summary above. Is conda actually free? tracks the current wording, and Anaconda’s terms of service is the authority for a compliance decision.
Which is faster?
uv wins, though conda’s reputation for slowness is now worse than its behavior. Building an environment with NumPy, pandas, scikit-learn, and matplotlib on a 4-vCPU Debian container gives these numbers. “Cold” means nothing has been downloaded yet; “warm” means the tool already holds the packages in its local cache:
| Step | conda (libmamba) | uv |
|---|---|---|
| Resolve only, cold | 4.9s | 1.2s |
| Create environment, cold cache | 40.4s | 4.0s |
| Create environment, warm cache | 15.5s | 1.3s |
The solve is not where conda loses. libmamba, conda’s default solver, resolved that stack in under five seconds. The remaining time goes to downloading and linking conda’s larger cross-language artifacts, which carry compiled libraries a wheel would leave to the system.
Quoted figures of “several minutes to solve” describe conda’s older pure-Python solver, which libmamba replaced. If you last measured conda before the switch, measure again.
How reproducible is each?
uv writes a lock file named uv.lock, which records the exact version of every package that gets installed, including the dependencies your dependencies pulled in, with a checksum for each. One file covers Linux, macOS, and Windows. Committing it to git makes uv sync rebuild the same environment on any machine.
conda has locking too, just not in environment.yml. That file is a wish list: it records the packages you asked for, not the exact set that got installed, so two conda env create runs weeks apart can produce different versions.
conda’s actual lock formats are narrower than uv’s:
conda list --explicitwrites exact package URLs that reproduce an environment byte for byte, on the same platform only.conda env exportpins versions and build strings, which is portable in principle and machine-specific in practice.- conda-lock writes a
conda-lock.ymlcovering several platforms from one file, which is the closest equivalent touv.lockand is a separate tool.
The gap is cross-platform locking in the box, not locking at all.
How the daily workflow differs
uv is project-local. It puts a virtual environment in a .venv folder inside the project directory, and uv run finds it automatically. There is no conda activate equivalent to remember, because switching projects is just changing directories. Nothing is installed system-wide, and the environment is disposable, since uv sync rebuilds it from the lock file.
conda defaults to named environments in one central directory: conda create -n myenv, then conda activate myenv to switch to it, from anywhere on the machine. That suits a researcher moving between many analyses, and it means the environment outlives any one directory. conda also supports project-local environments with conda create -p ./env, which behaves much closer to uv’s model.
The trade runs both ways. Central environments are reusable but easy to lose track of, and they make “which environment is this?” a question you have to answer. Project-local environments answer that question by construction, at the cost of duplicating large packages across projects.
What happens to your existing conda environments?
Nothing, unless you remove them. Adopting uv for a new project leaves every existing conda environment intact and usable.
For environments you want to move, the pieces port unevenly:
environment.ymlhas no import path. uv reads PyPI, not conda channels, so migration re-declares dependencies rather than converting a file. How to migrate from conda to uv walks through recovering the list, including thepip-installed packagesconda env export --from-historyleaves out.- Jupyter kernels that point at a conda environment keep working while that environment exists, and they break the moment you delete it. Register a replacement kernel from the uv project with
ipykernel, then remove the stale one withjupyter kernelspec remove <name>. - Environments you no longer recognize are worth auditing before converting anything. An environment whose dependencies are all PyPI wheels converts cleanly; one pulling GDAL or a CUDA toolkit from conda-forge should stay where it is, or move to pixi.
To keep working in an inherited conda environment rather than replace it, Take over an existing conda environment is the better path.
Can uv and Anaconda coexist?
They install separately and run side by side, but an Anaconda install changes what uv does by default. The installer adds a conda initialize block to your shell startup file (~/.bashrc or ~/.zshrc), and that block activates the base environment in every new terminal you open.
With base active, uv pip install declines to install anything:
$ uv pip install cowsay
error: No virtual environment found; run `uv venv` to create an environment, or pass `--system` to install into a non-virtual environment
uv does not count conda’s base as a virtual environment. A named conda environment gets the opposite treatment: activate one and uv pip install installs straight into it.
$ conda activate analysis
$ uv pip install cowsay
Using Python 3.12.13 environment at: /opt/conda/envs/analysis
Installed 1 package in 16ms
+ cowsay==6.1
Warning
That second behavior is a hazard for a conda environment you care about. conda’s solver does not track packages installed by uv or pip, so a later conda install can overwrite them or break their dependencies. Install into a conda environment this way only when you would have run pip install there anyway.
The second effect is interpreter inheritance. With base active, uv venv and uv add build the project’s .venv from Anaconda’s interpreter, and they prefer it even when uv has its own Python installed. Your project’s Python version follows whichever one Anaconda shipped. A teammate without Anaconda gets a different version from the same commands, and no warning marks the divergence.
uv python pin alone does not settle this. The pin requests a minor version, and Anaconda’s interpreter satisfies the request:
$ uv python pin 3.13
Pinned `.python-version` to `3.13`
$ uv sync
Using CPython 3.13.13 interpreter at: /opt/conda/bin/python3
Asking for a uv-managed interpreter explicitly is what breaks the link. Set it in pyproject.toml so the whole team inherits it:
[tool.uv]
python-preference = "only-managed"$ uv sync
Using CPython 3.13.15
Creating virtual environment at: .venv
uv venv --managed-python and UV_MANAGED_PYTHON=1 do the same thing for a one-off command. Once the .venv exists, uv run uses it regardless of which conda environment is active.
How does each handle CUDA, GDAL, and compiled dependencies?
This is conda’s strongest ground. conda resolves a native library, its command-line tools, and the matching Python binding together in one solver pass, which is why GDAL, HDF5, and CUDA toolchains have lived in the conda world for years.
uv installs compiled packages when they ship self-contained wheels, and more of them do every year. NumPy, SciPy, and rasterio bundle their native libraries into the wheel.
PyTorch is worth calling out, because it is the case most people are really asking about. PyTorch wheels bundle their own CUDA runtime, so uv needs no CUDA toolkit and no conda: only the host’s NVIDIA driver has to be new enough for the runtime the wheel carries. Point uv at the right index and it works. How to Install PyTorch with uv has the index configuration.
uv hits a wall when a package ships no self-contained wheel and needs a system library whose version must match exactly. The standalone gdal package, which provides the osgeo.gdal bindings, publishes only a source distribution and needs a matching libgdal already installed. The GDAL command-line tools (gdalwarp, ogr2ogr) sit outside the Python packaging world entirely, as do compilers and a full CUDA toolkit with nvcc.
For a package-by-package breakdown, see uv vs pixi vs conda for scientific Python.