How to dockerize a Django, Preact, and PostgreSQL Application

Dockerizing your Django application can be intimidating, but the rewards outweigh the risks. In this guide, Charlie Macnamara walks you through the setup process so you can get the most out of your applications.

During my recent deep dive into new technologies, I found the classic issues of integrating numerous tech tools effectively. I've written about my experiences to save you the trouble I had.

One essential component I've looked into is using Docker to implement containerization. While the initial setup takes a little longer, it significantly simplifies and optimizes your technological stack.

Prerequisites

To follow along with this tutorial, make sure you have the following:

We'll start a Django application and then a Preact application, containerize them both, run the containers, and then create an API to ensure the stack works correctly.

Getting started

To start, create an empty folder; we'll name ours django-preact-docker and navigate to this folder from the terminal.

We'll follow the first steps of creating a Django application - creating a virtual environment, activating it, and then installing Django.

(If virtualenv is not installed, run pip install virtualenv)

Run these commands in the terminal:

virtualenv venv
source ./venv/bin/activate

Then, with our virtual environment running, install Django along with some other dependencies:

pip install django django-cors-headers psycopg2-binary

Now, we can create a new Django project and get it running on our local server.

Setting Up Django

From the terminal, navigate to django-preact-docker, and run:

django-admin startproject backend

The above command creates a new folder, backend. Navigate into this folder:

cd backend

And then start the project:

python manage.py runserver

You can test if the server is running by going to: http://127.0.0.1:8000/

a screenshot showing the successful test server

Finally, create a requirements file that will hold our requirements for the project. While in django-preact-docker/backend, run:

pip freeze > requirements.txt

Setting up our Preact application

Head back to our root folder django-preact-docker.

In the terminal, run:

npm init preact

When prompted, change the project directory to "frontend," then hit enter for the rest of the options.

Once installed, from the terminal, navigate into django-preact-docker/frontend and run the following command to set up a development server:

npm run dev

Once the server has started, it will print a local development URL to open in your browser. Check if this is working before moving on!

a screenshot showing a successful precat response

Containerizing

Next, we must create configuration files, so Docker knows what to do.

Containerize Django

Navigate to django-preact-docker/backend and create a new file:

touch .dockerignore

Open the file and add the following:

# The file may be hidden on Finder; the default macOS shortcut is Shift-Command-. to show hidden files
venv
env
.env
Dockerfile

Next, run:

touch Dockerfile

Open it and add:

FROM python:3.8-alpine

ENV PYTHONUNBUFFERED 1

WORKDIR /app/backend

COPY requirements.txt /app/backend/

RUN apk add --update --no-cache postgresql-dev gcc python3-dev musl-dev

RUN pip install -r requirements.txt

COPY . .

CMD [ "python", "manage.py", "runserver", "0.0.0.0:8000" ]

In the above code:

  • FROM specifies the parent image that we'll be using
  • ENV sets the environment variable PYTHONUNBUFFERED to "1" to give us real-time log output.
  • WORKDIR specifies the working directory within the container.
  • COPY the requirements file to the working directory and later install the requirements.
  • RUN, install some more dependencies for psycopg-2,
  • COPY the content of our backend to the Docker container
  • The starting command for our container

Containerize Preact

Go to django-preact-docker/frontend and create a new file:

touch .dockerignore

Open the file in your text editor and add the following:

node_modules
npm-debug.log
Dockerfile
yarn-error.log

Go back to the terminal in frontend and create another Dockerfile:

touch Dockerfile

Open and edit "Dockerfile" to contain:

FROM node:16-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 5173

CMD ["npm", "run", "dev"]

Lastly, we must add the new npm run dev script. Head to frontend/package.json and change scripts too:

"scripts": {
  "dev": "vite --host 0.0.0.0",
}

Packaging our applications with Docker Compose

Next, we must create a configuration file to run our two Docker containers together. In the main folder, django-preact-docker, create a new file called docker-compose.yml:

touch docker-compose.yml

Open it and edit it to contain the following:

version: '3.9'

services:
  db:
    image: postgres:14-alpine
    ports:
      - '5432:5432'
    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_DB=postgres
    volumes:
      - ./data/db:/var/lib/postgresql/data/

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - '5173:5173'
    volumes:
      - ./frontend:/app/frontend
    depends_on:
      - backend

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_DB=postgres
    ports:
      - '8000:8000'
    volumes:
      - ./backend:/app/backend
    depends_on:
      - db

The Docker Compose file tells Docker how the different containers work together.

Build the containers

Go to the root django-preact-docker from the terminal and run the following command (this may take a while):

docker-compose build

Once complete, you should see the images in Docker Desktop.

You can then run the containers with the following:

docker-compose up

After this, the servers are accessible at the ports:

  • 5173 for frontend
  • 8000 for backend
  • 5432 for the database

You should check that they're running correctly first before moving on.

Press Ctrl + C or run docker-compose down to stop the containers.

Additional setup

We need to change a few settings,

Navigate to the backend folder and change settings.py to:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'postgres',
        'USER': 'postgres',
        'PASSWORD': 'postgres',
        'HOST': 'db',
        'PORT': '5432',
    }
}

Also, we need to change CORS further below in settings.py. The CORS configuration allows our different applications to communicate across different domains in the web browser:

INSTALLED_APPS = [
    'corsheaders',  # add this
]

MIDDLEWARE = [
    ...,
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    ...,
]

CORS_ALLOW_ALL_ORIGINS = True

Testing the stack works together

Finally, we'll build a simple response code to send data from the Django backend and ensure everything works together.

In backend/backend, create a new file, views.py, and paste the following:

from django.http import JsonResponse

def get_text(request):
    text = "All operational from the Django backend!"

    data = {
        'text': text,
    }

    return JsonResponse(data)

Then open urls.py and add these two lines:

from .views import get_text

And in URL patterns:

urlpatterns = [
    # other code
    path('test/', get_text),
]
from django.contrib import admin
from django.urls import path
from .views import get_text

urlpatterns = [
  path('admin/', admin.site.urls),
  path('test/', get_text),
]

You can now see this text on http://localhost:8000/test/.

To display it in our frontend, navigate to frontend/src/index.jsx and change it to the following:

import { render } from 'preact';
import preactLogo from './assets/preact.svg';
import './style.css';
import { useState, useEffect } from 'react';

export function App() {
    const [text, setText] = useState(null);

    useEffect(() => {
        fetch('http://127.0.0.1:8000/test/')
            .then(res => res.json())
            .then(data => {
                setText(data.text);
            });
    });

    return (
        <div>
            <h1>An unbeatable tech stack</h1>
            <a href="https://preactjs.com" target="_blank">
                <img src={preactLogo} alt="Preact logo" height="160" width="160" />
            </a>
            <p>Running Preact, Django, Postgres, and Docker</p>
            <p>{text}</p>
        </div>
    );
}

render(<App />, document.getElementById('app'));

Refresh and reload, and you should see a working Preact front page!

Conclusion

By containerizing your technology stack with Docker, you've taken a significant step toward seamless integration and enhanced efficiency. While the initial setup might have required some effort, the benefits are clear.

Now that you've containerized your tech stack, including Django, Preact, and Postgres, you can explore, innovate, and navigate the ever-evolving tech landscape more effectively—a pivotal moment in your tech journey. Embrace the possibilities, and may your adventures be characterized by streamlined integration and boundless creativity!

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. Get the Honeybadger newsletter
    Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial
    Stop digging through chat logs to find the bug-fix someone mentioned last month. Honeybadger's built-in issue tracker keeps discussion central to each error, so that if it pops up again you'll be able to pick up right where you left off.
    Start free trial