How Poetry’s lock file ensures reproducible Python builds
Learn how Poetry’s lock file records exact versions and hashes to give reproducible installs, what trade‑offs to watch for, and a step‑by‑step example you can run today.
01 Oct 2025, 11:59 UTC

Problem: dependency drift across machines
When developers add or update a package, the exact version that ends up installed can vary between laptops, CI runners, and production servers. This drift often appears as subtle bugs that are hard to reproduce because each environment resolves dependencies on the fly, picking the newest compatible version at install time.
Thesis: Poetry’s lock file records a deterministic set of versions that, given the same package index and a compatible platform, yields identical installations
The poetry.lock file captures the precise version, hash, and platform marker for every direct and transitive dependency. As long as the index state hasn’t changed and the target machine can provide the required wheels, poetry install will install the same set of packages.
How the resolver creates the lock file
Running poetry lock triggers these steps inside the project directory:
- Reads all constraints from
pyproject.toml, including version ranges, environment markers, and optional dependency groups. - Queries the configured package indexes (default PyPI) for candidate distributions that satisfy those constraints.
- Builds a dependency graph, respecting platform markers so only wheels compatible with the current OS/architecture are considered.
- Selects the newest version that meets all constraints and records its SHA‑256 hash.
- Repeats the process for every transitive dependency, producing a flat list of resolved packages.
The result is a single poetry.lock file. Unlike JSON, the lock file uses TOML syntax. An excerpt looks like this:
[[package]]
name = "requests"
version = "2.31.0"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.8"
[[package.file]]
file = "requests-2.31.0-py2.py3-none-any.whl"
hash = "sha256:1234567890abcdef..."
Platform‑agnostic installation with safety checks
Because the lock file stores platform markers, running poetry install on macOS, Linux, or Windows will automatically select the wheel that matches the target platform. If a required wheel is not available for that platform (e.g., a binary wheel only built for Linux), the command will fail with a clear error rather than silently installing an incompatible version.
Typical workflow:
- Add packages locally:
poetry add requests pydantic. - Generate the lock file:
poetry lock. - Commit both
pyproject.tomlandpoetry.lockto version control. - On another machine (CI or a colleague’s laptop), clone the repository and run
poetry install. The exact same versions will be fetched, provided the index hasn’t changed and the needed wheels exist.
Optional groups keep environments lean
Optional dependency groups are declared in pyproject.toml under sections like [tool.poetry.group.dev.dependencies]. The lock file records which packages belong to each group. Installing with --with dev (or omitting --no-root in CI) includes those groups; otherwise they are skipped, keeping the installed set minimal.
Pruning unused groups reduces lock‑file size only after the lock file is regenerated; the size reflects the total number of resolved packages, not just the groups.
Trade‑offs and practical considerations
- Lock‑file size: Projects with many optional groups or deep dependency trees can produce lock files of several tens of kilobytes. Removing groups that are never used and re‑locking shrinks the file.
- Resolution time: The resolver may take minutes on large graphs. In CI, caching the lock file (as an artifact) avoids repeated resolution, but any change to
pyproject.tomlstill requires a freshpoetry lockrun. - Upgrading dependencies: Running
poetry update <package>updates the lock file directly; a separatepoetry lockcommand is not required unless you want to resolve the whole graph again. - Index stability: Reproducibility is guaranteed only as long as the package index remains unchanged. If a new version of a dependency is published after the lock file was created, a later
poetry lock(orpoetry update) may produce a different set.
Worked example: from init to verified install
The following commands can be run on any machine with Poetry 1.8+ installed. No special privileges are needed; they operate within the project directory.
# 1. Create a new project (non‑interactive)
poetry init -n
# 2. Add runtime dependencies
poetry add requests pydantic
# 3. Generate the lock file
poetry lock
# 4. Commit the files to VCS
git add pyproject.toml poetry.lock
git commit -m "Add initial dependencies"
# 5. On a fresh machine (or CI):
# a. Clone the repository
# b. Install dependencies
poetry install
# 6. Verify that the installed tree matches the lock
poetry show --tree
To test that the lock file is deterministic, delete it and re‑run the lock step:
rm poetry.lock
poetry lock # regenerates the lock file
poetry install
poetry show --tree # should list the same versions as before, assuming the index unchanged
If the index has changed (a new version of a dependency was published), the second run may produce a different set, which is expected behavior.
Actionable take‑aways
- Always commit
poetry.locktogether withpyproject.toml. - Use
poetry install --no-rootin CI when you only need the dependencies (e.g., for building a wheel). - Periodically review optional groups; remove those that are never used and re‑lock to keep the file small.
- Cache the lock file in CI to avoid repeated resolver work, but refresh the cache whenever
pyproject.tomlchanges. - When upgrading, run
poetry update <package>(orpoetry updatefor the whole graph) and commit the updated lock file.
By treating the lock file as the single source of truth for dependency versions—under the assumption of a stable package index and a compatible platform—you eliminate the common “works on my machine” problem and ensure that every environment installs the exact same set of wheels.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.