Png - I resolved my issue with virtual env and Docker.
Title: Png - I resolved my issue with virtual env and Docker.
URL Source: https://www.pongi.it/posts/docker-1/
Markdown Content:
TL;DR: Add UV_PROJECT_ENVIRONMENT=/opt/venv to create the virtual environment outside the app directory so it won't be overwritten by bind-mounts in development.
While developing a Django web app, I rely on Docker both for development and production. The dev image can become, with minor modifications, a production image.
But I was having random issues with Python libraries, dependencies and even with Django itself. It became clear that the root of the issue was related to how the local development environment interacted with the container's virtual environment.
Let's look at a common project structure:
project-example/
├── example/ [<- Django root]
│ ├── .venv/
│ └── example/
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── manage.py
│ ├── pyproject.toml
│ ├── uv.lock [<- after installing dep]
│ ├── .gitignore
│ └── README.md
This Django project can be run by moving into the outermost example folder, installing dependencies and firing off your friendly manage.py runserver.
To build the container I tipically devfine a Dockerfile in the Django root that contains the instructions to build the image: it starts with a Debian image with Python and uv, install the dependencies in a virtual env and finally starts a gunicorn server pointed at the Django app. One key line is the COPY command which copies all my code from the current path into the container:
COPY . /example
This command would also copy my own virtual env (.venv) into the image, overwriting the image's internal one. This specific issue can be easily avoided by adding a .dockerignore file in the same directory (the Django root) with this content:
```
Dockerfile command COPY will skip this folder.
.venv ```
This image will be used mostly as-is for production. In my dev environment I mount the Django root path from my computer's file system onto the file system with a Docker compose.yaml.
services:
backend:
#...
mount:
# This bind mount is problematic.
- "./example:/example"
I can then run manage.py runserverinside the container, monitoring files from my file system and reloading the Django app as needed.
But here is the problem: when I mount this path, it carries the virtual env (the .venv/ path) along, messing with the container virtual env!
This is a problem that even the mighty Tiangolo faced. Docker suggests using compose watch, but it doesn't sync bi-directionally like a mount; specifically, files created inside the container are not mirrored back to my local file system, which makes obtaining Django migration files particularly difficult.
The fix came when I realized that instead of letting the bind-mount interfere with the virtual environment location, I simply asked uv to install the virtual environment in a dedicated path (/opt/venv). This completely isolates Python and dependencies, allowing me to happily mount my source code into the container without ever disrupting the required virtual environment structure.
Here's the relevant part of the Dockerfile:
``` FROM python:3.14-trixie AS base COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
...
Create the virtual environment outside the app directory
so it won't be overwritten by bind-mounts in development
ENV UV_PROJECT_ENVIRONMENT=/opt/venv
WORKDIR /example RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --locked --no-dev COPY . /example
...
Place executables in the environment at the front of the path
ENV PATH="/opt/venv/bin:$PATH"
...
```
If you've ever faced similar headaches integrating local machine file systems with Docker environments, I encourage you to dig into these containerization best practices! Get in touch if you have comments: you can find me on Mastodon.
Francesco Pongiluppi
LicenseSourceRSSMastodon This website does not use any form of cookies, nor does it collect any client data.