Django + Poetry + Docker

Nathan Clement · September 8, 2026 · 4 min read

Managing Python packages for Django made delightful

Cover image showing open books stacked on top of each other

Photo by Patrick Tomasso on Unsplash

Why use a Package Management System?

A new developer might start building a Django site by installing the latest version of Python from python.org, and using pip to install any necessary packages. Once local development is complete, they may deploy their code to a server prebuilt to host Python applications, only to see an Exception: Import Error: No module named django

This happens because no one installed Django on the host server.

To avoid this issue, they could define all imports in a Dockerfile. Something like this would work:

# ./Dockerfile
# pull official base image
FROM python:3.11-alpine

# set work directory
# set environment variables
# install linux dependencies

# install python dependencies
pip3 install -U pip
pip3 install Django=4.1.5
pip3 install some-package=1.0.0
pip3 install another-package=1.0.0

# copy project
# expose port
# run start command

This becomes painful to manage as soon as they start installing more dependencies, since they must carefully ensure that all local packages precisely match those in the Dockerfile.

If they start building another project that needs a different version of Python, or a different version of a package, the difficulty grows rapidly.

Several systems have grown within the Python ecosystem to alleviate the pain of package management complexity.

Virtual Environments

One of the most commonly referenced ways to manage Python packages and environments is to use the standard venv module.

This article does a great job explaining what Virtual Environments are in Python, and how to use them: https://realpython.com/python-virtual-environments-a-primer/

Virtual environments work great, but can also lead to frustration:

  • The version of Python is not included in requirements.txt — which can make upgrades less clear.
  • Your Virtual Environment will get out of sync if you install a new dependency and forget to run pip freeze > requirements.txt
  • Since requirements.txt includes all dependencies for your installed packages, it can be difficult to clean out a package that is no longer being used.

Introducing Poetry

Poetry manages the creation of virtual environments in a way that I have found both powerful and delightful.

Some highlights:

  1. Files called pyproject.toml and poetry.lock manage your environment in a way that feels similar to package.json and package.lock.json in Node.js
  2. Includes a powerful CLI tool reminiscent of npm
  3. Python is managed like a dependency by Poetry, allowing you to configure your Python version within the pyproject.toml file
  4. Scripts can be run with poetry run python script.py

Putting it all together

Imagine you have a simple Django application that you want to deploy as a Docker container, managed by Poetry.

Using Poetry Locally:

  1. Install Poetry locally curl -sSL https://install.python-poetry.org | python3 -
  2. Add Poetry to your path if it is not already there. I use zsh so I added the following to my .zshrc file: export PATH=”$PATH:/$HOME/.local/bin”
  3. Initialize Poetry poetry init — you can use the CLI prompts to interactively set up your dependencies, or add the later yourself
  4. Add dependencies (for example poetry add Django)
  5. Run scripts (i.e. poetry run python manage.py runserver )

Preparing to Deploy with Docker

Here is an example working Dockerfile:

# ./Dockerfile
# pull official base image
FROM python:3.11-alpine

# set work directory
WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1s

# install linux dependencies 
# these may vary by project
# this list is relatively lightweight
# and it handles most of what I need
RUN apk update && apk upgrade && \
  apk add --no-cache gcc g++ musl-dev curl libffi-dev postgresql-dev zlib-dev jpeg-dev freetype-dev

# install poetry to manage python dependencies
RUN curl -sSL https://install.python-poetry.org | python3 -

# install python dependencies
COPY ./pyproject.toml .
COPY ./poetry.lock .
RUN /root/.local/bin/poetry install

# copy project
COPY . .
# run at port 8000
EXPOSE 8000
CMD /app/scripts/start_server.sh

This script starts the Django server (per the Dockerfile above, I store it at ./scripts/start_server.sh

/root/.local/bin/poetry run python manage.py runserver 0.0.0.0:8000

In this case, /root/.local/bin/poetry lets me avoid adding poetry to the PATH in my container.

Deploy this container, and you are off to the races!


Let me know what you think about this approach, and if you see the benefit of a tool like Poetry, or if you have a different preferred Python package management strategy!


Originally published on 2023-01-31 on Medium.