# How to Migrate from conda to uv

This guide converts a [conda](https://pydevtools.com/handbook/reference/conda.md) environment into a [uv](https://pydevtools.com/handbook/reference/uv.md) project built on `pyproject.toml` and a `uv.lock` lockfile. The result: a project-local `.venv`, a cross-platform {{< term "lockfile" >}}, and resolves and installs that finish in seconds instead of minutes.

Unlike the [conda-to-pixi migration](https://pydevtools.com/handbook/how-to/how-to-migrate-from-conda-to-pixi.md), there is no `--import` flag. uv reads from {{< term "pypi" "PyPI" >}}, not conda channels, so the migration re-declares your dependencies rather than converting a file.

If you inherited a conda environment and want to keep working in it rather than replace it, start with [Take over an existing conda environment](https://pydevtools.com/handbook/tutorial/take-over-an-existing-conda-environment.md).

## Confirm uv can replace your environment

uv fits a project whose dependencies all live on PyPI as {{< term "wheel" "wheels" >}}: NumPy, pandas, scikit-learn, matplotlib, SciPy, and most of the scientific stack ship working wheels. For that stack, uv is the leaner choice and the fastest resolver available.

uv installs Python packages only. It cannot install the conda-only, non-Python dependencies conda-forge is built to provide:

- CUDA toolkits and GPU libraries resolved from conda-forge
- The standalone `gdal` package and its `osgeo.gdal` bindings, HDF5, or PROJ
- Compilers and non-Python languages (R, Julia) sharing one environment

If your environment pulls any of those from conda-forge, migrate to [pixi](https://pydevtools.com/handbook/reference/pixi.md) instead, which keeps the project-local, lockfile-driven workflow while installing from conda-forge. See [uv vs pixi vs conda for scientific Python](https://pydevtools.com/handbook/explanation/uv-vs-pixi-vs-conda-for-scientific-python.md) and [when should I choose pixi over uv?](https://pydevtools.com/handbook/explanation/when-should-i-choose-pixi-over-uv.md) to confirm the call.

Leaving the `defaults` channel behind also removes a licensing obligation. [Is conda actually free?](https://pydevtools.com/handbook/explanation/is-conda-actually-free.md) covers who owes Anaconda a paid license.

## Recover your dependency list

If the environment has an `environment.yml`, read the package names from it:

```yaml {filename="environment.yml"}
name: analysis
channels:
  - conda-forge
dependencies:
  - python=3.12
  - numpy
  - pandas
  - scikit-learn
  - matplotlib
  - pip:
    - seaborn
```

If the environment was built by hand with `conda install`, ask conda for the specs you asked for:

```console
$ conda env export -n analysis --from-history
name: analysis
channels:
  - conda-forge
dependencies:
  - python=3.12
  - numpy
  - pandas
prefix: /opt/conda/envs/analysis
```

> [!WARNING]
> `--from-history` reports only what conda installed. Packages you installed with `pip` inside the environment never appear, so a migration based on that output alone silently drops them. List them separately before you delete anything:
>
> ```console
> $ conda list -n analysis | grep pypi
> matplotlib                 3.11.1           pypi_0                pypi
> seaborn                    0.13.2           pypi_0                pypi
> ```
>
> That listing includes dependencies pulled in automatically. Carry over the packages you installed on purpose; uv resolves the rest.

## Create the uv project

Create the project with `--bare`, which writes a `pyproject.toml` and nothing else. The default `uv init` builds a distributable package: a `src/` layout, a `[build-system]`, and a console-script entry point, none of which an analysis environment needs. Pass the Python version from the `python=` line so `requires-python` matches:

```console
$ uv init --bare --name analysis --python 3.12
Initialized project `analysis`
```

Pin the interpreter so uv builds the `.venv` against it rather than the newest Python on the machine:

```console
$ uv python pin 3.12
Pinned `.python-version` to `3.12`
```

> [!NOTE]
> Pass `--python` to `uv init` before running `uv python pin`. Without it, `uv init` sets `requires-python` to the newest interpreter on the machine, and pinning an older version fails as incompatible with that floor.

## Add your dependencies

Pass every package name from your recovered list to a single `uv add`. Packages from a `pip:` section go through the same command, since they were already PyPI packages:

```console
$ uv add numpy pandas scikit-learn matplotlib seaborn
Using CPython 3.12.10 interpreter at: /usr/local/bin/python3.12
Creating virtual environment at: .venv
Resolved 20 packages in 427ms
Prepared 18 packages in 2.14s
Installed 18 packages in 242ms
 + matplotlib==3.11.1
 + numpy==2.5.1
 + pandas==3.0.5
 + scikit-learn==1.9.0
 + scipy==1.18.0
 + seaborn==0.13.2
```

uv writes the packages into `[project.dependencies]` and captures exact resolved versions in a lockfile (`uv.lock`):

```toml {filename="pyproject.toml"}
[project]
name = "analysis"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "matplotlib>=3.11.1",
    "numpy>=2.5.1",
    "pandas>=3.0.5",
    "scikit-learn>=1.9.0",
    "seaborn>=0.13.2",
]
```

Drop conda's pins unless something depends on them. A conda pin translates to a version constraint (`uv add "pandas==2.2.*"`), but old pins carried across wholesale tend to conflict with each other and fail to resolve. Start unpinned, confirm your code runs, then re-pin only what breaks.

`uv init --bare` writes no `.gitignore`. Add `.venv/` to yours so the environment stays out of version control.

## Map conda commands to uv

| Conda command | uv equivalent | Notes |
|---|---|---|
| `conda create -n analysis python=3.12` | `uv init --bare --python 3.12` | uv creates the `.venv` on the first `uv add` or `uv sync` |
| `conda activate analysis` | `uv run <command>` | `uv run python script.py` uses the project venv with no activation step |
| `conda install scipy` | `uv add scipy` | Adds to `pyproject.toml` and updates `uv.lock` |
| `pip install some-pkg` | `uv add some-pkg` | Both come from PyPI; one command now |
| `conda env export` | `uv lock` | `uv.lock` records the exact resolved versions |
| `conda env create -f environment.yml` | `uv sync` | Recreates `.venv` from `pyproject.toml` + `uv.lock` |
| `conda list` | `uv tree --depth 1` | Shows your declared dependencies; `uv pip list` shows everything installed |

To activate the environment the conda way instead of prefixing with `uv run`, source the venv directly: `source .venv/bin/activate` on macOS and Linux, or `.venv\Scripts\activate` on Windows.

## Move your Jupyter kernel

A conda environment registered as a Jupyter kernel needs an equivalent kernel pointing at the new `.venv`:

```console
$ uv add --dev ipykernel
$ uv run ipython kernel install --user --name=analysis
Installed kernelspec analysis in /Users/you/Library/Jupyter/kernels/analysis
```

Select the `analysis` kernel in Jupyter. Remove the stale conda kernel with `jupyter kernelspec remove <old-name>`. See [How to run a Jupyter notebook with uv](https://pydevtools.com/handbook/how-to/jupyter-notebook-with-uv.md) for launching notebooks from the project.

## Verify the environment and remove the old one

Recreate the environment from the lockfile and import the packages:

```console
$ rm -rf .venv
$ uv sync
Using CPython 3.12.10 interpreter at: /usr/local/bin/python3.12
Creating virtual environment at: .venv
Resolved 50 packages in 1ms
Installed 44 packages in 462ms

$ uv run python -c "import numpy, pandas; print(numpy.__version__, pandas.__version__)"
2.5.1 3.0.5
```

If `uv sync` reports `No pyproject.toml found in current directory or any parent directory`, you ran it outside the project. `cd` into the directory that holds `pyproject.toml` and try again.

Once the uv project works, tear down the conda environment:

```bash
conda deactivate
conda env remove -n analysis
```

Commit `pyproject.toml` and `uv.lock` so teammates reproduce the environment with `uv sync`. Keep the `environment.yml` around only while other team members finish switching.

## Learn More

- [How do uv and conda compare?](https://pydevtools.com/handbook/explanation/how-do-uv-and-conda-compare.md)
- [Which Python package manager should I use?](https://pydevtools.com/handbook/explanation/which-python-package-manager-should-i-use.md) for the full decision tree
- [Understanding the conda and Anaconda ecosystem](https://pydevtools.com/handbook/explanation/understanding-the-conda-anaconda-ecosystem.md) for where conda-forge fits
- [How to install Python with uv](https://pydevtools.com/handbook/how-to/how-to-install-python-with-uv.md) for managing interpreters without conda
- [How to migrate from requirements.txt to pyproject.toml with uv](https://pydevtools.com/handbook/how-to/migrate-requirements.txt.md) for the pip-based path
- [uv reference](https://pydevtools.com/handbook/reference/uv.md) for the full command set
