Welcome to our step-by-step guide on containerizing a Django application with PostgreSQL. If you’re new to Docker and want to understand the general concept before making your hands dirty, please follow this article

Prerequisites:

Step 1: Create a new project directory.

$ mkdir dockerize-django && cd dockerize-django

Step 2: Set up a virtual environment.

Create and activate a virtual environment named “ENV”.

$ python3 -m venv ENV
$ Source ENV/bin/activate. # On Windows: .\ENV\Scripts\activate

Step 3: Create a requirements.txt file and install dependencies. 

$ echo -e “Django==3.2.10\npsycopg2-binary==2.9.6 >> requirements.txt
$ pip install -r requirements.txt

Step 4: Create a Django project and app.

$ django-admin startproject devcolumn .
$ python manage.py startapp app1

Step 5: Initialize the “app1” in the settings.py.

Add ‘app1’ to the “INSTALLED_APPS’ section in ‘settings.py’.

Step 6: Create a URLS configuration in app1

Create a new file named ‘urls.py’ inside the “app1” directory.

Step 7: Create a templates directory

Create a directory called “templates” inside “app1” with a template file named “index.html”.

Step 8: Modify project and app URLs and views

In `devcolumn/urls.py`:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('', include('app1.urls')),
]

In `app1/views.py`:

from django.shortcuts import render

def index(request):
message = "Happy Dockerizing!!"
return render(request, 'index.html', {"message": message})

In `app1/urls.py`:

from django.urls import path
from app1.views import index

urlpatterns = [
path('', index, name='home'),
]
Now test your application:

Open your browser and navigate to http://localhost:8000 . You should see a “Happy Dockerizing!!” message.

Now comes the crucial step: containerizing our Django application and PostgreSQL using Docker. 

To begin this process, let's create a new file named "Dockerfile" in the root directory. This will serve as the blueprint for encapsulating our Django project into a portable and efficient container.

Dockerfile
# Stage 1: Build Stage

# Use the official Python slim image as the base image
FROM python:3.8-slim as builder

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Set the current work directory
WORKDIR /app

# Install dependencies
COPY ./requirements.txt .

RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt


# Stage 2: Production Stage

# Pull the official base image
FROM python:3.8-slim

# Install PostgreSQL client tools and ncat for network connectivity checks
RUN apt-get update && apt-get install -y postgresql-client ncat

# Create a directory for the app user
RUN mkdir -p /home/app

# Create a non-root user and group with explicit IDs
RUN addgroup --system docker --gid 1001 \
&& adduser --system --ingroup docker --uid 1001 docker

# Create the appropriate directories
WORKDIR /app

# Copy requirements
COPY --from=builder /app/wheels /wheels

# Install dependencies using wheels
RUN pip install --upgrade pip && pip install --no-cache /wheels/*

# copy entrypoint.sh
COPY ./docker/entrypoint.sh .
RUN sed -i 's/\r$//g' entrypoint.sh

# Grant execute permission to the entrypoint script
RUN chmod +x entrypoint.sh

# Copy the entire project
COPY . /app/

# Change ownership of all the files to the app user
RUN chown -R docker:docker /app/

# Change to the app user
USER docker

# run entrypoint
ENTRYPOINT ["./entrypoint.sh"]
CMD [ "run_devcolumn" ]

EXPOSE 8000

Next, create a new directory named "docker", where all the configuration related to the Dockerfile and docker-compose will be stored. Under this directory placed these three files:

dev.env
DJANGO_DEBUG=True
DJANGO_MODE=DEV

PGHOST=db
PGPORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=devcolumn
TZ=PST

db.env
PGHOST=db
PGPORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=devcolumn
TZ=PST
entrypoint.sh
#!/bin/sh

set -e

HELP_TEXT="

Arguments:
run_devcolumn: Default. Run the Devcolumn server
run_migrations: Run database migrations
help|-h: Display help text
"

# Function to display help text
display_help() {
echo "Devcolumn Management Script"
echo "${HELP_TEXT}"
}


# Function to wait for the database server to be available
wait_for_db() {
echo "Checking the availability of the database server..."
until ncat -z -w 1 "${PGHOST}" "${PGPORT}"; do
sleep 1
done
echo "Database server is up and running"
}

# Function to run database migrations
run_migrations() {
echo "----- RUNNING DATABASE MIGRATIONS -----"
cd /app/devcolumn
python manage.py migrate
echo "Database migrations completed successfully"
}


# Function to run the Django development server
run_django_server() {
echo "----- *** RUNNING DJANGO DEVELOPMENT SERVER *** -----"
exec python manage.py runserver 0.0.0.0:8000
}

# Function to run the Devcolumn application
run_devcolumn() {
wait_for_db

if [ "${DJANGO_MODE}" = "DEV" ]; then
# Create the database
python manage.py migrate
echo "Running Devcolumn in development mode..."
run_django_server
elif [ "${DJANGO_MODE}" = "PROD" ]; then
echo "Running Devcolumn in production mode..."
collect_static
fi
}

# If no arguments are supplied, assume the server needs to be run
if [ "$#" -eq 0 ]; then
run_devcolumn
fi


# Process arguments
while [ "$#" -gt 0 ]; do
key="$1"

case ${key} in
run_devcolumn)
wait_for_db
run_devcolumn
;;
run_migrations)
wait_for_db
run_migrations
;;
help|-h)
display_help
;;
*)
echo "Invalid command: ${key}"
display_help
exit 1
;;
esac
shift # next argument or value
done

Now, let's create our docker-compose file to run the two services "Django" and "PostgreSQL" with the below configurations:


docker-compose.yml
version: '3.8'

services:
devcolumn:
container_name: devcolumn
build:
context: .
dockerfile: ./Dockerfile
command: run_devcolumn
volumes:
- devcolumn-logs:/app/devcolumn/logs
ports:
- 8000:8000
env_file:
- ./docker/dev.env
depends_on:
db:
condition: service_healthy
networks:
- a-net


db:
container_name: db_devcolumn
image: postgres:15
volumes:
- postgres-data:/var/lib/postgresql/data
- postgres-log:/var/log/postgresql
ports:
- "5432:5432"
env_file:
- ./docker/dev.env
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
networks:
- a-net

networks:
a-net:
driver: bridge

volumes:
devcolumn-logs:
postgres-data:
postgres-log: 


Run the below build to build the Dockerfile run the services defined in the docker-compose file:

docker-compose up --build

Some useful commands

$ docker build -t <image-name> <path-to-dockerfile> # Build docker images
# docker ps # List running containers
# docker ps -a # List all containers
$ docker rm <container-id or container-name> #Remove containers
$ docker rmi <image-name or image-id> # Remove images
$ docker volume ls # List volumes
$ docker network ls # List networks
$ docker network inspect <network-name> # Inspect a network
$ docker stats <container-id or container-name> # View container resource usage
$ docker system prune # Prune unused resources
$ docker-compose up # Run containers in the background
$ docker-compose stop # Stop services
$ docker-compose down # Stop and remove containers
$ docker-compose exec python manage.py <mangement commands> # Run Django management commands.
$ docker-compose logs <service-name> # Display service logs
$ docker-compose exec <postgres-service-name> psql -U <postgres-user-name> # Connect to PostgreSQL using psql

Visit http://localhost:8000. You should see a “Happy Dockerizing!!” message.

Link to the GitHub repo can be found here. 

You have successfully containerised your first Django application along with PostgreSQL by following these steps. Congratulations on taking this important step towards building scalable and portable applications with Docker. Stay tuned for more advanced Docker tips and best practices to enhance your containerization journey. Happy coding!


Bishwo Bijaya Shah

devcolumn avatar

Software Engineer

© Copyright 2025. All Right Reserved.

Scroll to Top