How to set up docker compose with django and pdm

I have a django project with pdm and docker compose and I set up the codebase volume to enable django hot reload and debugging in the container. Building with the compose config works fine but when I try to run the server with docker compose up -d I hit a python error as if the libs were not picked up properly.

The project has the following architecture

project/
├── config/
│   ├── settings.py
│   └── urls.py
│   └── ...
├── some_django_app/
│   └── ...
├── compose.yaml
├── Dockerfile
├── README.md
├── pyproject.toml
└── pdm.lock

the compose file is as follows

services:
  web:
    build:
      dockerfile: Dockerfile

    command: pdm run python manage.py runserver 0.0.0.0:8000
    ports:
      - 8000:8000
    volumes:
      - .:/app
    env_file:
      - .env

my dockerfile is as follows

# Use an official Python runtime as a parent image
FROM python:3.13.2-slim-bullseye

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Set the working directory in the container
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Install PDM
RUN pip install --no-cache-dir pdm

# Copy the project files into the container
COPY . /app

# Accept build argument for ENVIRONMENT
ARG ENVIRONMENT=prod

# Install project dependencies using PDM
pdm install --prod --no-lock --no-editable;

Here is the trace of the error when I up the container

INFO: The saved Python interpreter does not exist or broken. Trying to find another one.
INFO: __pypackages__ is detected, using the PEP 582 mode
Traceback (most recent call last):
  File "/app/manage.py", line 12, in main
    from django.core.management import execute_from_command_line
ModuleNotFoundError: No module named 'django'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/app/manage.py", line 23, in <module>
    main()
    ~~~~^^
  File "/app/manage.py", line 14, in main
    raise ImportError(
    ...<3 lines>...
    ) from exc
ImportError: Couldn't import Django. Are you sure it's installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?

It is as if pdm couldn't pick up my local libs. When I remove the container line in my compose file and rebuild, django runs fine. What is wrong with my configuration?

The volumes: in your Compose file are hiding the virtual environment that pdm creates. The pdm documentation notes:

When you run pdm install the first time [...] PDM will create a virtualenv in <project_root>/.venv, and install dependencies into it.

This happens in the RUN pdm install line at the end of the Dockerfile. In the Compose file, though, you're bind-mounting the current host directory over all of /app in the container, and this hides the virtual environment too.

This bind mount effectively means you aren't using anything in your Docker image at all, and you're reintroducing the "works on my machine" problem that Docker typically tries to avoid. I'd remove this bind mount, and use a local Python virtual environment (maybe managed by pdm even) for day-to-day development. Make sure your Dockerfile declares a CMD

ENTRYPOINT ["pdm", "run"]
CMD ["./manage.py", "runserver", "0.0.0.0:8000"]

and then you can remove the parts of the Compose file that duplicate the image's settings – no volumes: or command:.

version: "3.8"
services:
  web:
    build: .
    ports:
      - 8000:8000
    env_file:
      - .env

Since pdm uses standard PEP 621 packaging metadata (that is, a pyproject.toml file with a top-level [project] key) you could also use standard Python tools (pip) in your Docker image, even if you use pdm for your local development setup. RUN pip install ... in a Dockerfile will generally install things into the "system" Python installation, but since your image only contains the single application, there's not a risk of conflict from doing this.

# Dockerfile
# Don't install pdm, but you should be able to
WORKDIR /app
COPY pyproject.toml ./  # maybe with some other files too, but not the whole project
RUN pip install -e .    # into the "system" Python
COPY ./ ./
# There's not a virtual environment so you don't need to do anything to activate it
CMD ["./manage.py", "runserver", "0.0.0.0:8000"]

(Coincidentally this last setup would work with a volumes: mount hiding the application code, since the library dependencies aren't installed in /app. There are still potential problems if the Dockerfile does any sort of manipulation on the source tree, or you leak things like __pycache__ directories into the container.)

Вернуться на верх