How to containerize Django applications for beginners

Is your Django project ready for deployment and are you wondering how to best deploy it? You might’ve heard a lot of suggestions and most if it are about using Docker. You’ve heard Docker before and you know it’s one of the ideal ways to deploy your application but you don’t have any idea how to do it. It might even sound like a dream to do it this way and it might seem that it’s hard to figure it out.

Don’t worry, you just need to experience how to do it and it will get easier as you develop in Docker more.

In this post, I’ll guide you through how you can containerize your Django project if you are a beginner and make it production ready.

Prepare your Django Project

Here’s what you need to prepare to get started:

Configure Django to manage static files

Next, we need to configure Django to manage the static files of your app — the CSS, JS, images, etc. We are doing this because we want Nginx to serve the static files which it does efficiently and securely.

To do this, we first need to import os in settings.py:

"""
Django settings for core project.

Generated by 'django-admin startproject' using Django 3.1.2.

For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""

from pathlib import Path
import os # add this

After that, we need to add the STATIC_ROOT value:

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

Lastly, create a directory named “static” in the same path as manage.py. Also, put a blank file inside named ".blank".

This is not required but I do this because I encountered issues with AWS ECS before where Django’s collectstatic command wasn’t able to create the directory and write the static files into it.

If you want to know more about how Django manages static files, this page helps a lot: https://docs.djangoproject.com/en/3.1/howto/static-files/

Install Docker

Download and install Docker Desktop. If you’re on Windows, please make sure you have WSL2 installed. For more information, please see this link: https://docs.docker.com/docker-for-windows/wsl/

Create an entrypoint.sh file in the root directory of your project

entrypoint.sh is a script that executes when you run your Docker image

#!/bin/sh

python manage.py migrate --no-input
python manage.py collectstatic --no-input --clear

exec "$@"

Let’s go through what the lines mean.

python manage.py migrate --noinput

Running migrate applies the migrations which means synchronizing the database state and with the current models. We’re running migrate to make sure that the DB and models are always in sync.

The parameter “–no-input” means it will set the interactive shell to False and will always choose True/Yes to the questions. We’re using this parameter so that our application won’t stop in case there’s a prompt from the interactive Django shell.

python manage.py collectstatic --no-input --clear

This command collects the static files and puts them into STATIC_ROOT to be served by Nginx as mentioned above. The –clear option means that we want to remove stale static files. We want to do this because we want our static files to be served by Nginx. More info about collectstatic can be found here.

Create a Dockerfile file

A Dockerfile is a file which contains all commands to build the image.

FROM python:3.8-slim AS base

# Setup env
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8


FROM base AS python-deps

# Install pipenv and compilation dependencies
RUN pip install pipenv

# Install python dependencies in /.venv
COPY djangoapp/Pipfile .
COPY djangoapp/Pipfile.lock .
RUN PIPENV_VENV_IN_PROJECT=1 pipenv install --deploy
RUN /.venv/bin/python -m pip install --upgrade pip


FROM base AS runtime

# Create and switch to a new user
RUN apt-get update && apt-get install -y make curl

RUN useradd --create-home appuser
WORKDIR /home/appuser
USER appuser

# Copy virtual env from python-deps stage
COPY --chown=appuser:appuser --from=python-deps /.venv /.venv
ENV PATH="/.venv/bin:$PATH"

# Install application into container
COPY --chown=appuser:appuser djangoapp /home/appuser

EXPOSE 8000

ENTRYPOINT ["sh", "entrypoint.sh"]
CMD ["gunicorn", "djangoapp.wsgi:application", "--bind", "0.0.0.0:8000", "--timeout", "300", "--worker-tmp-dir", "/dev/shm", "--workers=2", "--threads=4", "--worker-class=gthread"]

It might be a lot right now but to summarize, it builds the image instructing it to:

  • Build the image using python:3.8-slim as the base
  • Installs pipenv and uses it to install the virtual environment and the Python dependencies
  • Copies your application files and source to /home/appuser
  • When running, execute entrypoint.sh first
  • Then, run the application using Gunicorn, run it via port 8000 and use 2 workers and 4 threads (You can modify these. Fore more info on how many worker, threads and what other worker classes to use, please refer to: Gunicorn – Design)

We’ll discuss more about Dockerfiles in a future post.

Building the image

Make sure you’re in the directory where Dockerfile is found.

docker build --tag <image_name>:latest .

The –tag parameter is the ID of the specific image name that you’ve built. You’ll be using the value when running the docker image.

Running the image

Just for clarify the docker terms:

  • Image – your application all packaged up along with the stuff you instructed in the Dockerfile
  • Container – the running instance of your image

You can run the image by executing the following in the command line:

docker run -e "Key=Value" -e "AnotherKey=AnotherValue" -p 80:8000 <image_name>:latest 

Here’s what the parameters mean:

  • -e This defines the environment variables used by your application. Remember when we used environment variables instead of hard coding the values in settings.py? We are doing this because once the image is built, we can’t modify settings.py anymore. We need a way to set the values in settings in case there are changes without having to rebuild the image.
  • -p Tells docker to forward the host port 80 to the container’s port 8000. We instructed Gunicorn to listen to port 8000 in our Dockerfile

All Done

That’s it, your Django application is now Dockerized. I’ve also created a GitHub repository for your reference: https://github.com/asumawang/django-docker-demo you can use it as a guide on how to easily containerize your Django webapp.


Do you want more Python and Django tips and tutorials? Drop your email in the box below and we'll send new content straight to your inbox.

1 thought on “How to containerize Django applications for beginners”

Leave a Comment