brandonwie.dev
EN / KR
On this page
devops devopspythonuv

uv.lock Best Practice

Whether to commit `uv.lock` to version control.

Updated March 22, 2026 4 min read

I was setting up uv for a new ETL project and paused at .gitignore: should uv.lock be committed or ignored? The instinct from Python-land is to ignore dependency artifacts — .venv/ is always gitignored, and uv.lock feels similar. But uv.lock isn’t a virtual environment. It’s a lockfile, and the answer has real consequences for build reproducibility.

Why This Gets Confusing

Misleading analogy to .venv — Developers familiar with Python often equate uv.lock with .venv/ (which is gitignored). This leads to reflexively adding uv.lock to .gitignore, but they serve completely different purposes.

Large diffs on first commit — The initial uv.lock can be hundreds of lines for projects with many transitive dependencies. The first PR that adds it looks intimidating, triggering pushback from reviewers who don’t understand why it’s needed.

CI/CD silent divergence — Without a committed lockfile, uv sync in CI resolves dependencies fresh each run. Builds pass for weeks until a transitive dependency releases a breaking change, causing a mysterious failure with no code change in the repo.

Library vs application confusion — For libraries, the answer is genuinely debatable, which makes teams think it’s also debatable for applications. It’s not.

The Options

OptionProsCons
Commit uv.lockReproducible builds, drift detection, audit trailLarger diffs when dependencies update, merge conflicts on lock changes
Gitignore uv.lockCleaner diffs, no merge conflicts on lockNon-reproducible builds, silent dependency drift, CI surprises

The Rule: Commit It

For applications, always commit uv.lock. The reproducibility and safety benefits far outweigh the occasional merge conflict. Lock merge conflicts are trivially resolved by re-running uv lock.

Per uv official documentation:

“The lockfile should be checked into version control, allowing for consistent and reproducible installations across machines.”

Why Commit

BenefitDescription
ReproducibilitySame dependency versions across all machines
CI/CD safetyuv sync errors if lockfile doesn’t match pyproject.toml
Drift detectionCatches unintended dependency changes
Audit trailGit history shows when dependencies changed

Without a committed lockfile, two developers running uv sync on the same day might get different dependency versions if a transitive dependency released between their installs. In CI, this means a build can break without any code change — one of the hardest failures to diagnose.

The Correct Mental Model

uv.lock is NOT like node_modules or .venv (runtime artifacts that are regenerated locally). It IS like package-lock.json or poetry.lock (dependency resolution snapshots that ensure reproducibility). All of these should be committed:

  • uv.lock (uv)
  • package-lock.json (npm)
  • poetry.lock (Poetry)
  • Cargo.lock (Rust — for applications)

When It’s Debatable

Project TypeRecommendation
ApplicationsAlways commit
LibrariesDebatable — consumers generate own lockfiles

For libraries, consumers generate their own lockfile based on the library’s version constraints. Committing the library’s lockfile doesn’t affect consumers, but it does help the library’s own CI be reproducible. The trade-off is less clear-cut.

When to Use This

  • All Python application repositories managed with uv (APIs, CLIs, ETL pipelines, Airflow DAGs)
  • Monorepos where multiple services share a lockfile for consistent dependency versions
  • Any project with CI/CD pipelines that should produce identical builds across environments

When NOT to Use This

  • Published libraries — If you’re building a pip-installable library, consumers generate their own lockfiles; committing yours adds noise without benefit
  • Exploratory/throwaway scripts — Single-file experiments or Jupyter notebooks that will never be deployed don’t need lockfile discipline
  • Projects not using uv — If the project uses Poetry or pip-tools, commit poetry.lock or requirements.txt respectively; don’t mix lockfile strategies

Takeaway

Commit uv.lock for every Python application project. It’s the equivalent of package-lock.json — not .venv. The one-time cost of a large initial diff and occasional merge conflicts is vastly outweighed by reproducible builds, CI/CD safety, and dependency drift detection. When merge conflicts occur, resolve them by running uv lock — it regenerates the lockfile from pyproject.toml.

References

Comments

enko