Using persistent data in a dockerized Django development environment

Florian Abel
CodeX
Published in
3 min readJul 4, 2022

Run Django inside a Docker container, mirroring your codebase, for a clean development environment that reflects code changes in real-time.

Photo by Ian Taylor on Unsplash

Development environment

Having a clean development environment where you can specify exactly which system and packages are being used is vital. It prevents interference between packages being installed on the host machine and those being used for your project. Docker containers provide a confined, clearly specified and reproducible environment and file changes on the host do not affect the container. However, during development, our code base changes continuously. To reflect these changes our container needs to be rebuilt and rerun continuously.

Djangos development server

Django runs its own development server, monitoring file changes and restarting when necessary. Shielded from the outside, these file changes do not happen inside a classical container setup.

Local database setup

Furthermore, working with a local database file like SQLite3 (Djangos default), all changes like migrations or database entries will be wiped clean every time the container is stopped (or restarted). Migrations can be rerun, even though it’s quite a tedious task. Other data will be lost completely.

Mounting a volume to a container

Mounting the project folder and database as a volume to the Docker container gives it read and write access. In other words, existing data (our project files and database) can be accessed and edited by the container, as well as new data can be saved permanently.

Setting up the environment

Our project structure

We want to mount the entire content of our project folder, including the database file we are using, for local development.

|-django_docker_setup
|---django_docker_setup
|---example_application
|-----migrations
|-manage.py
|-requirements.txt
|-Dockerfile
|-Database-File
List of requirements for our project

Our Dockerfile

Dockerfile to run our Django application

Building and running our container

Mounting a volume

Mounting a volume is the preferred mechanism to make data, used or generated by the Docker container, persistent.

In order to mount the folder to our container, we are using the -v or --volume flag on the docker run and specify the origin and destination paths:

docker run -v <Path of folder to mount>:<Path to mount inside container>

Publishing the port

In order to reach our Django application from outside the container, we need to publish a port to the host. In this case, we are mapping the previously exposed container port (8000) to the HTTP port (80) of our host. The format we are using is the following, more possibilities can be found in the Docker docs.

docker run -p hostPort:containerPort

The full command:

docker build -t container-name .
docker run -v $(pwd):/home/ -p 80:8000 container-name

Since our Dockerfile lives within the project folder, we want to mount all data from within that folder inside the working directory we have specified in the Dockerfile.

  • $(pwd) prints the path to the current working directory
  • /home/ is the absolute path, as defined in our Dockerfile

Accessing the running container

At this point, your Django project is running within your container. To access the container we can use docker exec . It runs a command in a running container, allowing us to open a shell.

First, we need to find our container id, by running:

docker ps

which gives us something like this:

CONTAINER ID   IMAGE                        COMMAND                  CREATED         STATUS         PORTS                      NAMES3fe54fa54c56   container-name   "/bin/sh -c 'python3…"   2 minutes ago   Up 2 minutes   0.0.0.0:80->8000/tcp       priceless_robinson

With the container id 3fe54fa54c56 we can now open our shell and run our migrations or whatever else we desire:

docker exec -it 3fe54fa54c56 bash

Inside we can now make or run our migrations in a persistent way:

dockeruser@3fe54fa54c56:/$ python3 manage.py migrate

Changing files during runtime

Also, code changes made during runtime will be mirrored inside the container in real-time. Changing a file outside of the container will instantly be registered by the Django application, and the development server restarts.

Summary

With these few simple steps, you created an environment that does not interfere with your host machine's setup, while keeping the development process smooth and easy. The environment is confined from your host’s system, clearly specified and reproducible. At the same time, file changes outside your container are instantly reflected inside, while changes inside, like migrations or database entries, are made persistent beyond the life of the currently running container.

--

--

Florian Abel
CodeX
Writer for

Techie & Builder | Python, Flutter, Machine Learning/Data Science