Poetry’s Lock File: Deterministic Dependency Resolution in Practice
Poetry’s lock file records exact versions and hashes, uses a backtracking resolver to handle conflicts, and gates optional dependencies so reproducible builds are achievable across platforms—if you verify on each target OS.
30 Sept 2026, 11:31 UTC

The problem: divergent installs
When a team shares a pyproject.toml but each developer runs pip install independently, subtle version differences creep in. A transitive dependency may satisfy one constraint on Linux and another on macOS, leading to "works on my machine" bugs that surface only in CI. Poetry solves this by producing a single poetry.lock that records the exact version and hash of every package after a successful resolution.
How Poetry builds a deterministic lock file
Running poetry lock in a clean virtual environment triggers Poetry’s backtracking resolver. The algorithm evaluates all constraints at once—including version ranges, optional‑feature markers, and platform markers—and returns a single compatible set. The resulting lock file stores:
- Exact package name and version
- Source URL (PyPI or a custom index)
- SHA‑256 hash for integrity verification
- Metadata such as Python version markers and optional‑dependency flags
Because the lock file is committed to version control, every developer and CI job runs poetry install against the same immutable graph.
Resolving conflicts and optional dependencies
If two direct dependencies require incompatible versions of a shared transitive package, Poetry aborts the lock operation and prints a clear conflict report that identifies the offending constraints. Optional dependencies (declared under [tool.poetry.extras]) are not pulled into the lock file unless a feature is explicitly requested. This keeps the default lock minimal while still allowing reproducible installs for any combination of extras.
Worked example: adding an optional feature
Assume a project defines an extra called dev that adds pytest and black:
[tool.poetry.extras]
dev = ["pytest", "black"]
To generate a lock file that includes the extra without installing the project itself (useful in CI), run:
# Run from the repository root ()
# Requires: poetry installed, a virtualenv activated (or poetry will create one)
poetry lock --no-root --extras dev
After the command finishes, inspect poetry.lock and you will see entries for pytest and black with fixed versions and hashes. If you omit --extras dev, those packages are absent from the lock file, demonstrating the gating behavior.
Trade‑offs and limits
- Platform markers: The resolver evaluates environment markers (e.g.,
sys_platform == "win32") at lock time. A lock file created on Linux may omit Windows‑only wheels, so the same lock file can produce different package sets on another OS. Mitigation: runpoetry lockon each target platform or use a matrix in CI. - Lock time on large graphs: Projects with hundreds of dependencies can take tens of seconds to resolve. In CI,
poetry install --no-rootskips re‑resolving the root package and speeds up the step when only transitive deps matter. - Non‑PyPI indexes: If a dependency comes from a private index, Poetry records the source URL but cannot verify the hash unless the index serves a
.sha256file or you manually add the checksum. Missing hashes raise a warning on install. - Optional‑dependency drift: Forgetting to pass
--extraswhen locking can leave required functionality out of production environments. Document the required extras in your deployment scripts.
Verify reproducibility in your pipeline
- Lock once, commit: Run
poetry locklocally, review the diff, and commitpoetry.lock. - Cross‑OS smoke test: In CI, spin up a Linux and a Windows runner. On each, execute:
Compare the output; the package list and versions must match exactly.poetry install --no-root poetry show --tree | head -30 - Hash audit: Periodically run
poetry check --lock(or a custom script) to ensure every entry still has a valid SHA‑256 hash. - Extra validation: For each declared extra, run
poetry lock --no-root --extras <name>and confirm the lock file grows only by the expected packages.
Following these steps gives you a single source of truth for dependencies, eliminates "works on my machine" surprises, and makes audits straightforward.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.