Keep Production Python Images Lean with Poetry Dependency Groups
Poetry's dependency groups let you keep one lock file while installing only the packages a given environment needs. Learn how --only, --with, and --without work across Poetry 1.x and 2.x.
01 Apr 2026, 17:04 UTC

The bloat problem
Shipping a Python service often means fighting image bloat. You run poetry install during a Docker build, and your production layer ends up carrying pytest, black, and mypy because they share a project with your runtime dependencies. The lock file is correct, but the installed footprint is wasteful. Poetry's dependency groups offer a cleaner path: maintain one lock file for the whole project, but tell the installer which subset a given environment actually needs.
How groups are declared in pyproject.toml
Poetry models dependency groups under [tool.poetry.group.<name>.dependencies]. The top-level [tool.poetry.dependencies] table is the implicit main group. The legacy [tool.poetry.dev-dependencies] section is deprecated in favor of [tool.poetry.group.dev.dependencies], so new projects should use the grouped form.
A typical split might look like this:
[tool.poetry.dependencies]
python = "^3.11"
fastapi = "^0.110"
[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
[tool.poetry.group.docs.dependencies]
sphinx = "^7.0"
Run Poetry commands in the directory containing pyproject.toml. No elevated permissions are required; Poetry operates on the project's virtual environment.
Selective installs: --only, --with, and --without
Group flags change what is installed, not what is locked. poetry.lock still contains every group's resolution, so --only main saves install time and image size but not lock-file size or full resolution time.
--only: install exactly the listed groups (comma-separated).poetry install --only maingives you the production set.--with: add groups to the default set. Because the precise default set has shifted across Poetry 1.x and 2.x, pin the version and verify.--without: remove groups from the default set.
By default, poetry install also installs the current root package. When you want dependencies only, add --no-root.
Extras are not groups
Extras (optional = true plus [tool.poetry.extras]) are a separate mechanism. An optional dependency is an extra a consumer can request; a group is an install-time selection the maintainer controls. Do not use groups as a substitute for extras when you are publishing a library.
Version-sensitive caveats
The exact default set of groups installed by a bare poetry install, and the precise meaning of --with as "in addition to the default," have shifted across Poetry 1.x and 2.x. Do not assume; pin the version in CI and record poetry --version.
In recent Poetry 2.x releases, poetry export is provided by the poetry-plugin-export plugin rather than core. A bare install may not expose the command, so add the plugin explicitly if your pipeline needs a requirements.txt.
Worked example: lean production install
Suppose your pyproject.toml defines a main runtime set, a dev group, and a docs group. To preview what a production install would pull, run:
poetry install --dry-run --only main --no-root
Expected check: the dry-run output lists only packages from the main group and their transitive dependencies. Risk: if a package you expected is missing, confirm it is declared under [tool.poetry.dependencies] and not accidentally placed in a group.
When the preview looks correct, perform the install:
poetry install --only main --no-root
To verify the environment afterward, run poetry run pip list and confirm that pytest and sphinx are absent.
If your deployment platform cannot read poetry.lock, export a requirements file using the same group flags:
poetry export --only main -f requirements.txt -o requirements.txt
In Poetry 2.x, ensure poetry-plugin-export is installed before running this. After exporting, diff the output against a full export to confirm the subset is correct.
Trade-offs and limitations
Selective group flags do not shrink poetry.lock. Changing group membership—adding or removing a dependency in a group—requires re-running poetry lock so the lock file and pyproject.toml stay consistent; poetry install trusts the lock and will not silently re-resolve.
Group flags are not a security boundary. Transitive dependencies of an excluded group can still appear if another selected group requires them. If you need a hard guarantee about installed packages, inspect the resulting environment rather than relying on group names alone.
Actionable next steps
- Record
poetry --versionin your CI logs and pin that version. - Run
poetry checkafter any group edit to confirmpyproject.tomland the lock file agree. - Use
poetry install --dry-runwith your chosen flags before touching a real environment. - If you export requirements, diff the group-specific export against the full export to catch accidental inclusions.
One lock file, many install profiles—just remember that the flags control the environment, not the resolution.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.