How to Dockerize a Django Project and Deploy it to a Cloud Hosting Provider

This article will guide you through dockerizing a Django project and deploying it to cloud providers. If you're new to Docker, check out the Docker website for a quick introduction.

We'll cover two deployment scenarios:

  1. Deploying on a virtual machine (VM) or an IaaS service like AWS EC2. Here, we'll use Docker Compose to define all necessary services and configurations before deploying at the OS level.
  2. Deploying on a PaaS service, using Doprax Cloud as an example.

Both methods share some similarities, and you can find the complete project source code on GitHub.

Last updated: May 14, 2025

Prerequisites

  • Basic knowledge of Django
  • Basic knowledge of Docker

Create Django Project

You should first install Python on your computer. I would recommend installing all Python packages in a virtual environment. First, create a Python virtual environment using the command below:

python -m venv venv

To activate the virtual environment on Unix OS, use:

source venv/bin/activate

To activate the environment on a Windows device, use this instead:

source  venv/Scripts/activate

Next up is to install Django on your Python virtual environment. To do this, use this command:

pip install Django

Now create a Django project with this:

django-admin startproject {project name}

In this case, we chose the core for Django project and used:

django-admin startproject core

The structure of the project is:

project structure

To test if your project is working or not, you navigate to the core directory and run your app with this command:

cd core 
python manage.py runserver

Now your Django app runs on your local machine by default on port 8000. If you want it to run on another port, you can add your port on another server by running command:

python manage.py runserver 5000

Now go to 127.0.0.1:{your app port}, e.g., 127.0.0.1.5000 and you'll see this page.

running django server on port

You can change your project settings like which database you will use, what your secret key is, or what your hostname is on core/settings.py.

In the first step, you should change your ALLOWED_HOSTS  to your host IP address or use [‘*’] to allow all host IP addresses or names. Django by default uses SQLite for the database; nevertheless, we change to PostgreSQL in the settings.py file, like this:

DATABASES = {
 
    'default': {
 
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
 
        'NAME': '{db_name}',
 
        'USER': '{db_username}',
 
        'PASSWORD': '{password}',
 
        'HOST': '{db_hostname_or_ip}',
 
        'PORT': '{db_port}',
 
    }
 
}

But before you change this part, if you want PostgreSQL, you should install psycopg2 on your python virtual environment by using this command:

pip install psycopg2

This will install the PostgreSQL database on your machine or another server and create a database table in your database.

To install the database, I would recommend installing using Docker. We will explain how you can install PostgreSQL on your machine using Docker in this article.

Django app settings ready for Docker

The first step is to create an environment configuration file, which includes ALLOWED_HOSTS, database config, DEBUG, and SECRET_KEY. You can use decouple or use OS Liberty; in this case, we use OS; you can import and configure by using:

import os
os.environ.get("SECRET_KEY")

For using decuple, you should install the library. To install decouple, you should use pip install python-decouple command. You need to import decouple, using from decouple import config to change the config on /core/core/settings.py

For example, we need to use an external secret key on the .env file. We should change the secret key variable with SECRET_KEY = config(‘SECRET_KEY’) on the decuple case. Now we go to create an .env file for reading config data from this file.

First, if you want to use decouple, you should write “[settings]” on top of the .env file, and for config secret key, write SECRET_KEY=ARANDOMSECRETKEY on the next line of the. env file.

Now your secret key is reading from the .env file, and this makes your app more secure and helps you to make better Docker files.

Project requirements

To install the package, you use your project on the Docker container. You should freeze your pip package.

Create a file named requirements.txt and place it in the top-level directory of your source bundle.

To freeze your pip, you should use:

pip freeze > requirements.txt

Dockerfile for a Django project

Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.

By using "docker build .", users can create an automated build that executes several command-line instructions in succession.

Let’s write a Docker file for our Django application and explain how it works:

# base image
FROM python:3.9.7-buster
 
# options
ENV PYTHONUNBUFFERED 1
 
# Set working directory
RUN mkdir core
# set the working directory
COPY . /core/
# coppy commands 
WORKDIR /core
 
# update docker-iamage packages
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y netcat-openbsd gcc && \
    apt-get clean
 
# update pip 
RUN pip install --upgrade pip
# install psycopg for connect to pgsql
RUN pip install psycopg2-binary
# install python packages 
RUN pip install -r requirements.txt
# create static directory
RUN mkdir static
# RUN python manage.py collectstatic --no-input
EXPOSE 5000
CMD ["gunicorn","--bind", ":5000", "core.wsgi:application"]

Dockers need a base image, like Python, Ubuntu, or CentOS, as the first line of the file.

If you choose Python, it is easier, but you can choose Ubuntu if you want more flexibility. The best choice would be to use the Python base image and the Python 3.9.7-buster images.

Next is ENV PYTHONUNBUFFERED 1, which means Python output is logged to the terminal, allowing Django logs to be monitored in real-time. 

Next, we should create a working directory with the command "RUN mkdir core". “mkdir” is the UNIX command to create the directory.

In the next step, we should select WORKDIR/core; this means all commands we use are now working on this directory, and now we update the Python image depending on this part with this command:

RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y netcat-openbsd gcc && \
    apt-get clean

The next part is "COPY . /core", which means we copy all files in this directory.

Then we install all pip packages we use on this project. For this part, we first update pip with the command "RUN pip install –upgrade pip", and on the next step, we install all Python packages for pyscopge by using the command "RUN pip install psycopg2-binary".

For other packages, we use the command "RUN pip install -r requirements.txt".

Create a static file directory if you don’t have it in your source code with the command "RUN mkdir static".

Note: We configure the static root address in the Django settings file with this part of code:

STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

In the next step, we should expose Django Port with "EXPOSE 5000". This configures your Django application on the port you want.

Based on this example, we run our app on the 5000 port. To use Gunicorn in this step, you should install Gunicorn with the command "pip install gunicorn" before you use pip freeze, and you just run Gunicorn with CMD Docker command like this: (CMD run your command on the last step.)

CMD ["gunicorn", "--bind", "0.0.0.0:5000", "core.wsgi:application"]

You can use your main project’s root wsgi in this case, on core/wsgi.py 

Now you can deploy your app on some PaaS cloud provider like Doprax, Microsft Azure, or AWS.

You can upload or clone your code on these platforms. We'll explain some of these platforms and how you can deploy your Django on the Paas platform in another article.

To test on your local machine, you can run:

docker run docker build --tag django_app:latest .

Then, run:

docker run --name django_app -p 8000:8000 django_app:latest

In your docker-compose

After installing Docker and docker-compose on your system, you can write and test your docker-compose if you want to deploy your app on IaaS, AWS EC2, or any other platform.

To begin, you will create one docker-compose file (this is a configuration file with .yml or .yaml format). Here we explain all parts of this file, but before that, we will explain the Django project structure:

2nd project structure

We will now cover docker-compose:

version: '3'
 
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - static:/app/staticfiles
    restart: always
    ports:
      - "5000:5000"
    networks:
      - web_nw
      - nginx_nw
    depends_on:
      - db
  db:
    image: postgres:10
    container_name: postgres
    volumes:
      - postgres:/var/lib/postgresql/data
    restart: always
    env_file: .env
    networks:
      - web_nw
  nginx:
    container_name: nginx
    build:
      context: nginx/
      dockerfile: Dockerfile
    ports:
      - "80:80"
    volumes:
      - static:/app/staticfiles
    networks:
      - nginx_nw
 
volumes:
  postgres:
    external: true
  static:
    external: true
networks:
  web_nw:
    external: true
  nginx_nw:
    external: true

First, choose the Docker Compose version—here, we use version 3.

Next, define the services your application needs. In this case, we include:

  • PostgreSQL (database)
  • Django application
  • Nginx (to serve static files)

You can add other services like Redis or a database client if needed.

Configuring the Django web app

  • The web service in the docker-compose.yml file includes the build section, which tells Docker to use a Dockerfile to build the application.
  • The context specifies the working directory, and the Dockerfile path defines how the image should be built.

Setting up volumes

  • Volumes ensure data persistence (e.g., user uploads).
  • Static files should be served using Nginx, so we mount a volume specifically for them.
  • If using an external volume, make sure to create it before running Docker Compose.

Restart policy & port papping

  • The restart policy is set to always, meaning the container restarts automatically if it stops unexpectedly. However, if manually stopped, it only restarts when the Docker daemon restarts or you manually restart it.
  • Port mapping is required to access the container. Since containers don't expose ports by default, we map port 5000 in the container to port 5000 on the host machine so the application can be accessed externally.
ports:
      - 5000:5000

The .env file that we configured in the first step is giving the Django application to Docker. As a result, we wrote this section of the config file and specified where the .env file can be found.

env_file: .env

Next, configure the Docker network to allow communication between your database and web app.

Since we are using an external network (external: true), you must create the network manually before running docker-compose.

You'll also need to configure your web app and Nginx to work with this network. The network definition should look like this:

networks:
      - web_nw
      - nginx_nw

The "depends_on" setting ensures that a service starts after its dependencies, but it does not wait for them to be fully ready. For example, if your web app depends on a database, depends_on will only ensure that the database has started, not that it's ready to accept connections.

If you need to ensure a service is fully ready before another starts, refer to Controlling startup order for solutions.

Here’s how to configure 'depends_on':

depends_on:
      - db

Now, let's define the database service. In this case, we're using PostgreSQL. Here’s the configuration:

image: postgres:10

This means your machine will pull the official PostgreSQL Docker image from Docker Hub. You can refer to the official PostgreSQL Docker documentation here for more details.

According to the PostgreSQL documentation, the directory /var/lib/postgresql/data should be mounted as a volume. This ensures that your data remains intact even if the database container stops or restarts.

To define this volume, add the following to your docker-compose.yml file:

volumes:
      - app_postgresql:/var/lib/postgresql/data

This way, your database data will persist across container restarts.

To create your PostgreSQL volume, we follow the same restart policy and network configuration used for the Django application.

Now that we've set up the database, the final step is configuring the Nginx web server. We chose Nginx for this project to serve static files and handle reverse proxying.

To get started with Nginx, create an Nginx directory inside your project and add a Dockerfile inside it. Here’s how:

FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Now that you're familiar with all parts of this file, it's time to write the Nginx configuration file.

In the second line of this file, we copy the configuration into the Nginx config directory.

We'll write the Nginx config file here, but in the next article, we'll provide a detailed explanation of each section. For now, we'll focus on the key parts needed to successfully deploy your Django application.

worker_processes 8;
user root;
error_log  /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {}
http {
    include mime.types;
    server {
        listen 80; 
        server_name web; # copy Your server name here
        location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            proxy_pass http://web:5000; # copy main hostname here
        }
        location /static/ { # copy your STATIC_URL here
            alias /core/staticfiles/; # copy your static file path here
        }
    }
}

In this file, we configure key parameters such as server_name, static file location, and proxy_pass.

  • The server_name should be set to your domain, e.g., example.com.
  • The proxy_pass directive maps incoming requests to the correct application port. Since our Django app runs on web:5000, we set proxy_pass to http://web:5000. This ensures that Nginx forwards requests to the correct service.
  • The static file location defines where Nginx serves static files. In Django’s settings.py, STATIC_URL is typically set to /static/, so we configure Nginx accordingly:
    • The location directive specifies the URL path for static files (/static/).
    • The alias directive defines the actual file path where static files are stored.

We also define the required volume and network settings. In this case, we use an external volume and network to allow other containers to access them.

To create networks and volumes in your Docker setup, you can use these commands:

docker network create {network_name}
docker volume create {volume_name}

Now you can deploy all your projects using the following code:

docker-compose up -d

However, to complete the remaining configuration, you need to execute commands inside the Django Docker container using the following command:

docker exec -it djangoapp-01_web_1 bash

Once inside the container, you need to run the following commands to set up your Django application:

  • "python manage.py makemigrations" → Creates new migration files based on the changes made to your models.
  • "python manage.py migrate" → Applies and re-applies migrations to set up your database schema.
  • "python manage.py collectstatic" → Gathers all static files into the STATIC_ROOT directory for deployment.
  • "python manage.py createsuperuser" → Creates a superuser account for accessing the Django admin panel.

With this setup, you can now use a Dockerfile or docker-compose to deploy your Django web application on any machine efficiently.

Deploy on Doprax

To deploy your Django app on Doprax, you simply need your Nginx settings, your Django app source code, and Dockerfile.

Sign up on Doprax, create an app space and then an app, and then import the source code from your GitHub repository.

In the next step, you should add your environment variables.

add key and value to env variable

To add variables to your Django app, just add them under the Environment Variables tab.

add variables to env var tab

By adding a new service and selecting PostgreSQL from the services list, you will now be able to create a database service, in this case, PostgreSQL.

list of services

Now you should configure your database username, password, and table like this:

select postgresql service

config postgres username and password

Creating an Nginx service, mounting your Nginx config file, and copying your Nginx config file to this path will be your next step.

mount nginx service

It is easy to create new volumes and shares between any services on the Nginx service and your main app by clicking on the volumes tab.

You can now see your public URL and service status in the Deploy section.

And to run some commands, you'll just need to click on open-shell and write your commands, if you want to migrate your database or create a superuser.