Using Poetry Lock Files and Dependency Groups for Reproducible Python Installs
Learn how Poetry's lock file and dependency groups give you reproducible Python installs and smaller deployment artifacts, with a concrete example and version-specific cautions.
13 Sept 2025, 04:28 UTC

The problem: environments drift between local work and CI
When a Python project grows, developers often notice that a test passes on their laptop but fails in the pipeline, or that a deployment pulls in an unexpected version of a library. The root cause is usually an unresolved dependency graph: pyproject.toml specifies version ranges, but the exact versions installed can differ each time an install runs.
Thesis: lock the graph and split dev vs. runtime deps
Poetry addresses both issues by (1) generating a poetry.lock file that pins every transitive dependency with its content hash, and (2) letting you declare optional dependency groups (e.g., dev, test) so that poetry install --only main installs only what production needs. The result is a deterministic install that can be reproduced on any machine and a smaller artifact for deployment stages.
How Poetry's lock file works
When you run poetry lock, Poetry resolves the full dependency tree from pyproject.toml, records the exact version of each package, and stores content hashes for the distributions. Committing poetry.lock to version control is the key practice: it guarantees that poetry install produces the same environment on another workstation or in CI, provided a compatible Poetry version is used.
Dependency groups: separating concerns
Starting with Poetry 1.2, the older [tool.poetry.dev-dependencies] section was replaced by named groups. A typical configuration looks like this:
[tool.poetry.dependencies]
python = "^3.9"
requests = "^2.28"
[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
flake8 = "^6.0"
Groups are independent; you can install any combination with flags such as --only dev, --without dev, or --only main (the main group contains the dependencies declared under [tool.poetry.dependencies]).
Worked example: setting up a reproducible project
Run these commands in a terminal on your development machine. No special permissions are required; Poetry manages its own virtual environments by default.
- Create a fresh directory and initialize Poetry (the
-nflag accepts defaults non-interactively):
mkdir demo-proj && cd demo-proj
poetry init -n
- Add runtime and dev dependencies:
poetry add "requests^2.28"
poetry add --group dev "pytest^7.0"
- Generate and inspect the lock file:
poetry lock
Open poetry.lock and confirm it contains [[package]] entries with exact pinned versions and hash metadata for each package and its transitive dependencies.
- Install only production dependencies in a clean environment and verify dev tools are absent:
poetry install --only main
poetry show --only dev # expect no output if dev group was never installed
If you previously ran a full install in the same environment, use a fresh virtual environment for this check, since --only main does not necessarily remove already-installed dev packages.
- Commit the lock file:
git add pyproject.toml poetry.lock
git commit -m "Add Poetry lock file and dependency groups"
Trade-offs and limitations
- Version-specific CLI flags: The group syntax and
--only/--withoutflags became stable in Poetry 1.2, and installer defaults have changed across releases. Always runpoetry --versionand consultpoetry install --helpto confirm the flags available in the version you actually run. - Platform awareness: The lock file records environment markers active at lock time. If you lock on Linux with one Python version and install on another OS or Python version, resolution can differ. To avoid surprises, lock in an environment that matches your deployment target.
- Caret range semantics: Poetry's
^constraint treats 0.x versions differently than many developers expect (e.g.,^0.3allows only 0.3.x, not 0.4). Check constraints carefully for pre-1.0 packages. - Third-party tooling: Poetry is not part of the Python standard toolchain. Teams standardizing on
pip,uv, orhatchmay face migration friction when adopting Poetry-generated lock files.
Actionable closing
To start benefiting from reproducible installs and slimmer deployments:
- Add
poetry.lockto your repository if it isn't already there. - Move linters, test runners, and local development utilities into dependency groups.
- In your CI pipeline, use
poetry install --only mainfor build and deploy stages, reserving full installs for test jobs. - Document the required Poetry version in your contributing guide and verify it in CI with a
poetry --versionstep.
By locking the dependency graph and separating concerns with groups, you eliminate a major source of environment drift while keeping deployment artifacts lean.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.