Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: February 21, 2025
When working with Django in a Dockerized environment, we need to ensure that database migrations are performed smoothly. However, it’s common to encounter a problem where Django fails to detect changes to models. In turn, these forces developers to recreate containers each time they modify the database schema.
In this tutorial, we’ll set up a simple Dockerized Django application and discuss how database migrations work within Docker. To demonstrate, we’ll work in the Linux environment.
To discuss migrations within Docker, let’s use PostgreSQL and Docker Compose to set up a simple Django project. Let’s also make sure that Docker, Docker Compose, and Python are installed on our system.
First, let’s create the project directory for our Django application and navigate into it:
$ mkdir dockerized-django-app && cd dockerized-django-app
After this, let’s set up the virtual environment:
$ python3 -m venv venv && source venv/bin/activate && pip install django psycopg2-binary
Now, we can create the Django project:
$ django-admin startproject myproject .
Here, we create the Django project myproject inside the dockerized-django-app project directory.
Next, let’s modify myproject/settings.py to use PostgreSQL:
from pathlib import Path
import os
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('POSTGRES_DB', 'mydatabase'),
'USER': os.getenv('POSTGRES_USER', 'myuser'),
'PASSWORD': os.getenv('POSTGRES_PASSWORD', 'mypassword'),
'HOST': 'db',
'PORT': '5432',
}
}
We configure Django settings to enable working with PostgreSQL.
In this step, let’s create a Dockerfile to define the Docker image for the Django application:
FROM python:3.9
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
In addition, let’s create a requirements.txt file.
django
psycopg2-binary
So, we include the necessary dependencies in the requirements.txt file.
Here, we create the docker-compose.yml file:
version: '3.9'
services:
db:
image: postgres
restart: always
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
volumes:
- postgres_data:/var/lib/postgresql/data
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/app
ports:
- "8000:8000"
depends_on:
- db
environment:
DATABASE_URL: postgres://myuser:mypassword@db:5432/mydatabase
volumes:
postgres_data:
This ensures that Docker Compose helps us manage the Django app and PostgreSQL database.
To start the services we run:
$ docker-compose up -d
...
Creating dockerized-django-app_db_1 ... done
Creating dockerized-django-app_web_1 ... done
Then, to run migrations:
$ docker-compose run web python manage.py migrate
This command creates the database schema inside the PostgreSQL container if the migration is successful.
Now, let’s see what happens when we add a new model.
In myproject directory, let’s add the books Django app:
$ docker-compose run web sh -c "python manage.py startapp books && ls -l && mv books myproject/"
Creating dockerized-django-app_web_run ... done
total 28
-rw-rw-r-- 1 root root 174 Feb 10 17:30 Dockerfile
drwxr-xr-x 3 root root 4096 Feb 12 10:59 books
-rw-rw-r-- 1 root root 514 Feb 10 18:12 docker-compose.yml
-rwxrwxr-x 1 root root 665 Feb 10 17:18 manage.py
drwxrwxr-x 3 root root 4096 Feb 12 10:54 myproject
-rw-rw-r-- 1 root root 23 Feb 10 17:38 requirements.txt
drwxrwxr-x 4 root root 4096 Feb 12 10:53 venv
This is what the command does:
Now, the books directory is also present locally in the myproject directory. Let’s proceed to register books in the inside the settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myproject.books',
]
Inside settings.py, we modify INSTALLED_APPS.
Also, we need to ensure that the name holds the value ‘myproject.books’ in myproject/books/apps.py:
from django.apps import AppConfig
class BooksConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myproject.books'
From here, we can proceed to define a new model.
Once we create the app, let’s navigate to books/models.py and modify it:
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
published_date = models.DateField()
def __str__(self):
return self.title
Here, we define the Book model.
After defining the Book model, let’s create and apply database migrations:
$ docker-compose run web python manage.py makemigrations books
Creating dockerized-django-app_web_run ... done
Migrations for 'books':
myproject/books/migrations/0001_initial.py
- Create model Book
This generates a new migration file inside the books/migrations/ folder.
Thereafter, let’s apply the migration:
$ docker-compose run web python manage.py migrate
Creating dockerized-django-app_web_run ... done
Operations to perform:
Apply all migrations: admin, auth, books, contenttypes, sessions
Running migrations:
Applying books.0001_initial... OK
Above, we update the PostgreSQL database schema.
Let’s start by modifying the books/admin.py file to register the model:
from django.contrib import admin
from .models import Book
admin.site.register(Book)
The above modification enables us to add books and manage them through the UI. However, we need to add a superuser to see this addition:
$ docker-compose run web python manage.py createsuperuser
Creating dockerized-django-app_web_run ... done
Username (leave blank to use 'root'): samuel
Email address: [email protected]
Password:
Password (again):
Superuser created successfully.
Once this is done, let’s log into http://localhost:8000/admin/ and confirm that the Book model exists under the section Books.
In a Dockerized Django project, it’s common for Django to fail to detect model changes. To be specific, makemigrations doesn’t generate new migration files. This can be a result of how Docker handles caching or even volumes.
For instance, if we modify the existing model books/models.py:
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
published_date = models.DateField()
isbn = models.CharField(max_length=13, unique=True) # New field
Then, we proceed to create migrations:
$ docker-compose run web python manage.py makemigrations books
If Django fails to detect the change, we can try a few troubleshooting steps.
Firstly, we can manually check whether Django detects the change:
$ docker-compose run web python manage.py makemigrations --dry-run
Django doesn’t recognize the model change if the command doesn’t display any output.
Secondly, we need to ensure the Django app is added to INSTALLED_APPS. In our project, we need to open settings.py and confirm that INSTALLED_APPS lists ‘myproject.books’.
Thirdly, we can check if the migration directory exists. We can check if books/migrations/ contains migration files don’t exist. If not, we can try:
$ docker-compose run web python manage.py makemigrations books
We can also try restarting containers:
$ docker-compose down && docker-compose up -d
Well, this removes outdated volumes and cached data.
Lastly, we can manually delete and recreate migrations:
$ rm -rf books/migrations/*
Above, we delete the content in books/migrations/. To follow this, we then recreate the migrations:
$ docker-compose run web python manage.py makemigrations books && docker-compose run web python manage.py migrate
These steps enable the proper detection of the model changes and the correct application of the migrations. For this reason, the database remains in sync with the Django models while running inside Docker.
In this article, we explored how to effectively handle database migrations using Docker Compose and PostgreSQL in a dockerized environment.
We worked on setting up a dockerized Django project, configuring PostgreSQL, and running migrations smoothly. By providing troubleshooting steps to tackle the issue of Django failing to detect model changes, we ensure database schema updates are applied correctly.
Now, we can maintain a smooth workflow when managing database changes in a containerized Django application. Properly handling migrations with Docker significantly improves the development experience, eliminating the need for container recreation, reducing downtime, and ensuring database consistency across different environments.
The code samples are available over on GitHub.