Safeguard
Developer Tools•

How do I move requirements.txt to uv without changing versions?

Import requirements.in into a uv project and pass your old requirements.txt as constraints so uv.lock keeps the exact versions you already run.

Safeguard Security Team
4 min read

Create a pyproject.toml with uv init, then import your loose requirements with uv add and pass the old pinned requirements.txt as constraints. That makes uv.lock reuse the versions you already deploy instead of solving for newer ones. Importing the pinned file alone, or the loose file alone, gives different results.

The trap in a naive import

A pip and pip-tools project usually has two kinds of files. The requirements.in file lists what you want, often without exact versions. The requirements.txt file is the compiled output with every package pinned. The uv migration guide points out the nuance: if you import requirements.in, uv solves for new versions because that file does not pin them. The environment you ship tomorrow is then not the environment you shipped yesterday.

The fix in the guide is to add your locked versions as constraints, so the existing versions are retained when uv produces the lockfile.

Steps

  1. Create a branch and keep requirements.txt in place for now.
  2. Run uv init if there is no pyproject.toml.
  3. Import the loose requirements with the pinned file as a constraint.
  4. Do the same for development files, using the dev group.
  5. Run uv lock and review the lockfile.
  6. Compare the versions in uv.lock with the old requirements.txt (see the diff method in the linked post below).
uv init
uv add -r requirements.in -c requirements.txt
uv add --dev -r requirements-dev.in -c requirements-dev.txt

The guide also shows a variation for a dev file that includes the base file with a line starting with a dash r. Strip that line before piping to uv add, otherwise the base requirements land in the dev group too.

What you gain and what changes

BeforeAfter
requirements.in and requirements.txtpyproject.toml and uv.lock
One compiled file per platformOne universal lockfile
Separate files for dev groupsDependency groups in one place

The uv documentation says its lockfile is always universal, so multiple files are not needed for each platform, and it can represent arbitrary groups. It also supports pinning a package to a specific index, which a requirements.txt cannot express. That index pinning is a real supply chain benefit, covered in pip extra index url versus uv first index.

After the import

Keep the old files for one release as a fallback, but stop editing them. Two sources of truth drift apart quickly. The uv documentation says a lockfile is not considered outdated just because new package versions were released, so nothing upgrades until you ask. Plan upgrades separately, as described in migrate and upgrade in separate pull requests.

Try it yourself

Click through it at your own pace, or open it full screen. Compare the inventory view with your old requirements.txt: the names and versions should line up.

Safeguard's dependency scanning and SBOM generation can inventory a Python project on either side of the move. A free scan is available at gold.safeguard.sh/scan.

Quick check

Why not just import requirements.txt directly?

You can, and it pins everything, but every transitive package becomes a direct dependency in pyproject.toml. Importing the loose file with the pinned file as constraints keeps your intent clean and your versions the same.

Will uv upgrade packages on its own later?

No. The uv documentation says new releases do not make the lockfile outdated. You upgrade when you choose to.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.