How to dockerize a Django project and deploy it to a cloud hosting provider

This article will cover how to dockerize your Django project and deploy it to cloud providers. If you are not familiar with Docker, you can read Docker[Link of Docker article] to give you a good sense of how it works. We will cover two cases. The first case is deploying a Django web application on an OS-level such as a VM or an Iaas service like AWS EC2 on this AWS EC2; on this step, we write one docker-compose and write all services and config we need on this step, and they deploy on the OS level. The second case is the same Django web application deployed on a PaaS service. We will use the Doprax Cloud platform as our PaaS service. A few parts of this two-step are the same. You can see this project source code on GitHub.

0- create Django project

You should first install Python on your computer. I would recommend installing all Python packages in a virtual environment. For using your python virtual environment, you can use

python -m venv venv

to create a virtual environment. Moreover, for activating the virtual environment on Unix os, use :

 source venv/bin/activate

and on windows machine :

 source  venv/Scripts/activate

for Install Django, you just need to use :

pip install Django

 Now you need to install Django on your python virtual environment. Now we need to create a Django project by

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:

To test your project is working or not, you should go on a core directory and run your app by this command:

 cd core 
 python manage.py runserver

Now your Django app runs on your local machine by default on 8000 port if you want to be run on another port you can add your port on fun server command like :

 python manage.py runserver 5000

and go to 127.0.0.1:{your app port} and you must see this page :

You can change your project setting 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 default uses SQLite for the database; nevertheless, we change to PostgreSQL by this part of code on settings.py file :

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

pip install psycopg2

installing the PostgreSQL database on your machine or another server, and creating a database table on your database. For installing the database, I would recommend installing using docker. We will explain how you can install PostgreSQL on your machine by docker in this article.

1- Django app settings ready for Docker

The first step is to create an environment config 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 config by

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 write .env file for reading config data from this file; first,  if you want to use decuple, you should write “[settings]” on top of the .env file, and for config secret key, write SECRET_KEY=ARANDOMSECRETKEY on the next line of .env file now your secret key read from .env file, and this makes more secure your app and help you to make better docker file 

2- 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. The following is an example requirements.txt file for Django. To freeze your pip, you should use

pip freeze > requirements.txt

3- 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. 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 or 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 write create a working directory with 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 user now working on this directory, and now we update the python image depending on this part of the 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 means we copy all files on this directory, and in the next step, however, we should install all pip packages we use on this project. For this part, we first update pip with RUN pip install –upgrade pip, and on next step, we install all python packages for pyscopge; we use RUN pip install psycopg2-binary, and for other packages with RUN pip install -r requirements.txt  on the next step, you create static file directory if you don’t have it on your source code with RUN mkdir static 

Note: we config static root address on 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 for with EXPOSE 5000 following configures your Django application on the port you want based on this example we run my app on 5000 port. To use gunicorn on 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 prover like doprax, Microsft Azure or AWS; you can upload or clone your code on these platforms. We explain some of these platforms and how you can deploy your Django on the Paas platform on another article, and for testing on your local machine, you can run

 docker run docker build --tag django_app:latest . 

and

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

4- In your docker-compose:

After installing Docker and docker-compose on your os, 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 with, 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:

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

You should choose the docker-compose version; we chose version 3, and in the next step, specify the number of services you’d like. We just write Postgres database and Django application and Nginx configuration on this file. You can set any other service you want, like Redis, a database client, or other services. For the Django web app, we write configuration on the web part of this file one thing you see on the web part is the build stage that’s A sign of building a dockerfile context main show your working directory path, and on the following line, you write your dockerfile path. The Dockerfile section explained how to write Dockerfiles for Django applications. In the next step, you write volume configuration to be careful you should volume your essential, and you don’t want loos data like your photo client upload on your app, and if you want to use a web server, you should serve static file on a web server like Nginx. There is only one volume to mount static files for serving with Nginx. Be careful if you are using a True external volume since you would have to create your volume just for running your Dock-Compose file on the next step; we config restart policy we chose restart always police this restart the container if it stops. If it is manually stopped, it is restarted only when the Docker daemon restarts or the container itself is manually restarted. Because you use docker and container concept, none of your container ports you can see on your machine Except you expose or map your container port manually. For this reason, we map the 5000 container port to the 5000 machine port by this part of configuring file

    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 could be found.

env_file: .env

In the next part of this file, you should config your docker network to make a network between your database and your web app. Then you need to config your web app and Nginx for this part because we use external true for network type (we’ll explain in the next section); you should create your volume before you start your docker-compose file for config your networks. You just need to write your network like this :

    networks:
      - web_nw
      - nginx_nw

Dependency between services means that depend_on does not wait for the database to be “ready” before starting the web – only until it has been started. If you need to wait for a service to be ready, see Controlling startup order for more on this problem and strategies for solving it. We config depends_on by

   depends_on:
      - db

Now we write database service  we use Postgresql for database on this case we just write

    image: postgres:10

That’s mean your machine pull Postgres official docker image from the docker hub; you can see Postgres official docker image doc on this link  on the Postgres document say /var/lib/Postgresql/data must be volume for volume data on your machine that’s because if your database goes down, your data will not be lost you just write

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

To create your Postgres volume, we use the same Django restart policy network to create your Postgres volume. 

We configure the database, and now we go to the last step and config the Nginx webserver. We chose Nginx for this project; for now, we start config Nginx by creating an Nginx directory on the project and writing Dockerfile for Nginx on this directory like this :

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

You familiar with all parts of this file now we should write Nginx config file and on line two of this file  copy this file on Nginx config directory we write Nginx config file here we explained all parts of this file on next article just we explain important part you need to deploy your Django app :

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
        }
    }
}

On this file, we just config a few spatial parameters kike server_name and static location file and proxy_pass  on server_name you should write server name like example.com on other parameters proxy_pass that’s the main map your URL on 80 port of Nginx on this case our app run on web:5000, and that’s mean we should write http://web:5000 on this parameters. The last part describes which URL we can see static files. In such case, we explain static_url and static directory on setting.py file for STATIC_URL is “/static/” we write next to the location and for statics file path we write next to alias.

We explain what kind of volume and network we want. In the aforesaid case, we use external volume and network to see your network and volume on any container you want to create. At other times and other containers that you will create for creating network and volume, you just need to use these two 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, for the rest of the configuration, you must use exec to Django docker container by

 docker exec -it djangoapp-01_web_1 bash

And run some commands like:

python manage.py makemigrations -> which is responsible for creating new migrations based on the changes you have made to your models.

Python manage.py migrate -> which is responsible for applying and reapplying migrations

python manage.py collectstatic ->  Collects the static files into STATIC_ROOT.

python manage.py createsuperuser -> create a superuser for the Django admin panel 

Now you can use dockerfile or docker-compose to deploy any Django web app on any machine

How can 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 your first project, and then import the source code from your GitHub repository.

In the next step, you should add your Environment Variables  on the next part like this:

To add variables to your Django app, just add them under the Environment Variables 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.

Now you should configure your database username, password, and table as follows this picture :

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

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 part:

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

And for a run some command, you just need to click on open-shell and write your commands like migrate your database or create a superuser.

Please do tell us, did you find the content above helpful?