- estimated reading time: 3 Minutes

How to use poetry to install dependencies into a specified Venv

The issue

So lets say You have a python project that needs to be run in a docker container and You have poetry set up for dependency management and to build the entire thing. Lets say you also added a previous poetry project as local dependeny because its proprietary code and You don’t happen to have a Python package repo for Your organization.

If you are like me, Your previous Dockerfile for a Poetry project might have had something along the lines of this in it:

RUN apt install -y gcc \
    && pip install poetry \
    && python -m venv /venv \
    && poetry export -f requirements.txt | /venv/bin/pip install -r /dev/stdin

But for some reason, the poetry maintainers decided, that one should no longer to be able to use the poetry export function by default and the plugin isn’t installed in the default pip package any more. This is how one can achieve something similar.

What is needed?

Poetry will install Dependencies in an activated environment if one exists and otherwise create one by itself. Sadly there is no option, to force the environment into a specfic path, as the maintainers deemed this functionality unneccessary.

Option 1: Simply install the old plugin (if You don’t have local dependencies)

The simplest option: You can always just reinstall the plugin before running the export command:

RUN apt install -y gcc \
    && pip install poetry \
    && python -m venv /venv \
    && poetry self add poetry-plugin \
    && poetry export -f requirements.txt | /venv/bin/pip install -r /dev/stdin

The problem i ran into here, is that now the installation is delegated to pip and i used a subdirectory as package. Pip does not like this.

Option 2: Activate venv before running poetry

This is what i settled on later. It is really just that simple, as long as poetry is inside an activated venv. The only issue is, that to activate the venv bash is needed, and the default shell used in RUN is /bin/sh (so probably ash, as the python container is based on Debian).

RUN python -m venv /venv \
    && bash -c "source /venv/bin/activate && /venv/bin/pip install poetry && /venv/bin/poetry install --no-root"

This will then properly install all the dependencies into the activated venv with poetry.

Full Dockerfile example with Option 2

A full example of my default multistage build setup for projects with poetry from now on.

FROM python:3.12 AS base

RUN apt update \
    && apt install -y <system deps>

FROM base AS pyinstall

# Build tools
RUN apt install -y --no-install-recommends build-essential gcc meson ninja-build

# this can force Poetry to not create its own virtualenv. I have not tested, how well this works without this
# option, better leave it disabled

ENV POETRY_VIRTUALENVS_CREATE=0

WORKDIR /app
COPY ./pyproject.toml ./poetry.lock .
COPY ./<local package> /app/<local package>
# possible Space savings: only install poetry in the pyinstall stage in the global
# python env, so that the venv REALLY only has the deps of the program
RUN python -m venv /venv \
    && bash -c "source /venv/bin/activate && /venv/bin/pip install poetry && /venv/bin/poetry install --no-root"

# Entrypoint
FROM base AS runner 

WORKDIR /app
# Copy Venv files + Project Files into the runner
# /venv should contain every needed folder at this point
COPY --from=pyinstall /venv /venv
COPY ./<project-dir> /app/<project-dir>

CMD [ "/venv/bin/python", "-m", "<project-dir>" ]

Hopefully, i could help anyone who was as lost as me!