Docker Compose for Django development#
I had to get Docker Compose working for a Django project, primarily to make it easier for other developers to get a working development environment.
Some features of this project:
- Uses GeoDjango, so needs GDAL etc for the Django app plus a PostgreSQL server running PostGIS
- Already has a
Dockerfile
used for the production deployment, but needed a separate one for the development environment - Makes extensive use of Django migrations (over 100 and counting)
I ended up with this docker-compose.yml
file in the root of the project:
1version: "3.1"2
3volumes:4 postgresql-data:5services:6 database:7 image: postgis/postgis:13-3.18 restart: always9 expose:10 - "5432"11 ports:12 - "5432:5432"13 volumes:14 - postgresql-data:/var/lib/postgresql/data15 environment:16 POSTGRES_USER: postgres17 POSTGRES_DB: mydb18 POSTGRES_PASSWORD: postgres19 web:20 container_name: myapp21 platform: linux/amd6422 build:23 context: .24 dockerfile: Dockerfile.dev25 command: python manage.py runserver 0.0.0.0:300026 environment:27 DATABASE_URL: postgres://postgres:postgres@database:5432/mydb28 DEBUG: 129 volumes:30 - .:/app31 ports:32 - "3000:3000"33 depends_on:34 - migrations35 - database36 migrations:37 platform: linux/amd6438 build:39 context: .40 dockerfile: Dockerfile.dev41 command: python manage.py migrate --noinput42 environment:43 DATABASE_URL: postgres://postgres:postgres@database:5432/mydb44 volumes:45 - .:/app46 depends_on:47 - database
The db
container runs PostGIS. It uses a named volume to persist PostgreSQL data in between container restarts.
The web
container runs the Django development server, built using the custom Dockerfile.dev
Dockerfile.
The migrations
container simply runs the apps migrations and then terminates - with depends_on
used to ensure that migrations run after the hdatabase server starts and before the web server.
Both web
and migrations
include a platform: linux/amd64
property - this ensures they will work on M1 Macs even if the Python dependencies are not yet easily compiled for that architecture, see Running Docker on an M1 Mac.
The container_name: myapp
field on the web
container is a convenience which means you can later run commands like this:
1docker exec -it myapp ./manage.py collectstatic
Here’s Dockerfile.dev
:
1FROM python:3.9-slim2
3ENV APP_HOME /app4WORKDIR $APP_HOME5
6ENV PYTHONUNBUFFERED 17
8# gdal for GeoDjango9RUN apt-get update && apt-get install -y \10 binutils \11 gdal-bin \12 libproj-dev \13 git \14 && rm -rf /var/lib/apt/lists/*15
16COPY requirements.txt .17RUN pip install --no-cache-dir -r requirements.txt18
19WORKDIR $APP_HOME/myapp20
21RUN ./manage.py collectstatic --no-input22
23CMD python manage.py runserver 0.0.0.0:3000