How to Deploy Django Channels on Docker using Nginx, Gunicorn and supervisor – in an easy way

Hang tight as I will teach you how to deploy Django Channels fast. Django Channels is a package for allowing WebSockets and other asynchronous protocols to be used in a Django application. It is built on top of the Django web framework and allows for the creation of real-time applications such as chat applications, online games, and more. Channels provides a channel layer that acts as a kind of “inbox” for messages that are sent asynchronously between different parts of your application, allowing for more efficient communication between the client and server.

Channels wraps Django’s native asynchronous view support, allowing Django projects to handle not only HTTP, but protocols that require long-running connections too — WebSockets, MQTT, chatbots, amateur radio, and more.

This tutorial will take you through the process of deploying a chat app implemented using Django-channels on the Doprax cloud platform. The process of implementing the chat app can be found in the Django-channels documentation here. You can stop at tutorial 2 in the link.

The Deployment Process

I want to believe we have gone through the tutorial in Django-channels tutorial. To deploy the app that we have implemented locally, there are certain things we have to do. We will first perform these things for Development Purposes and then go on to things to do for production.

Deploy on Doprax for Development purposes

Deploying on Doprax requires us to dockerize our project. So we’re going to create Dockerfile and doprax.yaml file in the project directory.

project directory

From the image above. You will notice the name of my Django project is chatProj and I have an app named chat.

In the Dockerfile put the below code.

FROM ubuntu:22.04


RUN apt-get update && apt-get install -y python3 python3-pip python3-venv
RUN python3 -m venv chat_env

WORKDIR /code

# install dependencies
COPY requirements.txt /code/
RUN /chat_env/bin/pip install -r requirements.txt

# Copy project
COPY . /code/

# Expose ports
EXPOSE 8000 

RUN chmod +x /code/start.sh
ENTRYPOINT ["./start.sh"]

So above we are telling docker to:

  • Set our base image as ubuntu version 22.04 image. 
  • Install python3, python3-pip, python3-venv.
  • Create a virtual environment named chat_env.
  • Create our working directory  /code. 
  • Copy our requirements.txt file into the working directory. 
  • Install all packages in the requirements.txt file.
  • Copy all our folders to the working directory.
  • Expose port 8000.
  • Execute the instructions in a file named start.sh . 

Create start.sh file in your project directory also. Put the below code.

full project directory
#!/bin/bash
source /chat_env/bin/activate
cd /code

echo "----- Collect static files ------ " 
python manage.py collectstatic --noinput

echo "-----------Apply migration--------- "
python manage.py makemigrations 
python manage.py migrate


echo "-----------Run django local server--------- "
python manage.py runserver 0.0.0.0:8000

Above we are:

  • Activating our environment  
  • Serving our static files
  • Running our migrations
  • Running normal runserver command for Django

The reason for running “python manage.py runserver” is that for our WebSocket app to work we need daphne. Daphne is the protocol server for asgi applications that Django channels use. So since we have daphne in INSTALLED APPS in settings.py, it will take over the “python manage.py runserver” command . This is what makes this method of deploying your Django-channels app Development purpose.

In doprax.yaml file, put the below code.

volumes:
  - name:  static
    mount: /staticfiles/

services:
  - redis:
      tag: 5

Above we are:

  • Creating volumes named static, where our static files will be persisted.
  • Using Redis since the Redis-channel layer is used in our CHANNEL_LAYER_BACKEND as in Django-channels tutorial documentation.

Great! we have set up the important files needed for deployment on doprax. There are just 3 more things to do.

First, in the Django-channels tutorial, in the room.html file, there is a line that establishes a WebSocket connection but uses “ws://” when creating the path. We have to change it to “wss://”. “ws” means “WebSocket” while “wss” means “WebSocket secure”.

js websocket connection
line 17 in the image

The second thing is to go to the settings.py file and change the “host” in the CHANNEL_LAYERS to “redis” instead of “127.0.0.1”. We are doing this because when we add redis service on doprax the hostname will be “redis”.

channel layers setting

The third thing is to allow doprax in the ALLOWED_HOST setting in the settings.py file. You can do that by putting ‘*’, meaning allow all, or ‘.doprax.com’, meaning allow only those with doprax.com domain.

We are done! Now push your code to GitHub and head over to the Doprax cloud platform.

Doprax platform

Doprax is a cloud platform for hosting your websites, apps, and APIs. 

Create your account on Doprax and head to the Account section and connect your GitHub account.

connect github doprax

Create an App by heading to the Dashboard section and clicking NewApp Give it a title and Description.

create new app doprax

Click on the created app and click on import from my GitHub account.

import github code doprax

Import the repository you created on your GitHub.

Import GitHub code page

After importing your code from GitHub you will notice there is a message sayingdoprax.yaml dependency file detected. Do you want to create dependencies created in it?”. Click Yes create them.

accept create dependencies doprax.yaml

Go to the Services section. You will notice Redis is automatically set up for you. This is because we allowed Doprax to create the dependencies in doprax.yaml file.

redis services doprax
The Hostname is redis.

Now go to the Volumes section, and you will also notice it is automatically set.

static file volumes

Finally, head to the Deploy section and press the play button.

Deploy Django Channels powered chat app
Congrats, it is deployed!

Deploy on Doprax for Production purposes

In this part, we will use supervisor, Nginx to ensure gunicorn and daphne work properly.

Supervisor is a client/server system that allows its users to control a number of processes on UNIX-like operating systems. It will help start and manage gunicorn and daphne for our chat app.

Nginx is a web server that can also be used as a reverse proxy, load balancer, mail proxy and HTTP cache. It will act as our load balancer, it will route HTTP requests to gunicorn and WebSocket requests to Daphne.

With that understanding; we will create Dockerfile in the project directory to dockerize the app, create doprax.yaml since doprax uses it to set up volumes and services automatically for us, create various .sh and .conf files to make sure daphne and gunicorn works.

In your Dockerfile add the below code

FROM ubuntu:22.04


RUN apt-get update && apt-get install -y python3 python3-pip python3-venv supervisor

ARG USER=root
USER $USER
RUN python3 -m venv chat_env

WORKDIR /code

# install dependencies
COPY requirements.txt /code/
RUN /chat_env/bin/pip install -r requirements.txt

# Copy project
COPY . /code/

COPY deployment deployment

COPY deployment/gunicorn.conf /etc/supervisor/conf.d/gunicorn.conf
COPY deployment/daphne.conf /etc/supervisor/conf.d/daphne.conf


RUN mkdir /logs
# Expose ports
EXPOSE 8000 
EXPOSE 8001

RUN chmod +x /code/start.sh
RUN chmod +x /code/deployment/start_app.sh
RUN chmod +x /code/deployment/start_daphne.sh


ENTRYPOINT ["./start.sh"]

So in the above, we are telling docker to:

  • set our base image as ubuntu  version 22.04 image. 
  • install python3, python3-pip, python3-venv and supervisor
  • setting user as root
  • create a virtual environment named chat_env.
  • create our working directory  /code. 
  • copy our requirements.txt file into the working directory. 
  • install all packages in the requirements.txt file.
  • copy all our folders to the working directory.
  • copy deployment folder to deployment
  • copy the config files in the deployment folder to the conf.d folder after supervisor is installed.
  • create logs directory. This is to keep log files of daphne and gunicorn
  • expose port 8000 (for gunicorn)
  • expose port 8001(for daphne)
  • Give permissions for executing the .sh files.
  • execute the instructions at the start.sh file. 

In your start.sh file add the below code.

#!/bin/bash
exec /usr/bin/supervisord -n 

Above we are starting supervisor.

In your doprax.yaml file add the below code

volumes:
  - name:  static
    mount: /staticfiles/

services:
  - redis:
      tag: 5

Above we are:

  • Creating volumes named static, where our static files will be persisted.
  • Using Redis since the Redis-channel layer is used in our CHANNEL_LAYER_BACKEND as in Django-channels tutorial documentation.

Now create deployment folder in your project directory and create the following files; daphne.conf, gunicorn.conf, start_app.sh, start_daphne.sh

project directory

So what we are doing is creating conf files for daphne and gunicorn which supervisor will use. The start_app.sh file contains the command to start gunicorn while start_daphne.sh file contains the command to start daphne.

In gunicorn.conf file add the below code

[program:gunicorn]
command=/code/deployment/start_app.sh
directory=/code/chatProj
user=root
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile = /logs/gunicorn.log

Above we are:

  • Creating a supervisor program and calling it gunicorn
  • Telling supervisor the command to run to start gunicorn program. The command is in the start_app.sh file
  • Defining directory of our project
  • Setting user as root
  • Telling supervisor to autostart and autorestart the program
  • Defining where logs to go to

In daphne.conf add the below code

[program:daphne]
command = /code/deployment/start_daphne.sh
directory=/code/chatProj
user = root
autostart=true
autorestart=true
stdout_logfile = /logs/daphne.log
redirect_stderr = true

Above we are:

  • creating a supervisor program and calling it daphne
  • telling supervisor the command to run to start daphne program. The command is in the start_daphne.sh file
  • defining directory to our project
  • setting user as root
  • telling supervisor to autostart and autorestart the program
  • defining where logs to go to

In start_app.sh add the below code

#!/bin/bash
source /chat_env/bin/activate
cd /code

echo "----- Collect static files ------ " 
python manage.py collectstatic --noinput

echo "-----------Apply migration--------- "
python manage.py makemigrations 
python manage.py migrate


echo "-----------Run gunicorn--------- "
gunicorn -b :8000 chatProj.wsgi:application

In start_daphne.sh add the below code

#!/bin/bash
source /chat_env/bin/activate
cd /code

daphne -b 0.0.0.0 -p 8001 chatProj.asgi:application

So we have done the major work of starting gunicorn and daphne and it is managed by supervisor

What remains is our nginx acting as a load balancer. We are going to setup on doprax as a service

Now push what you have done to GitHub and head over to the Doprax cloud platform.

Nginx on the Doprax platform

Follow the processes in the Doprax Platform section of this tutorial. After importing your code repository and allowing doprax to create dependencies in doprax.yaml.

Head over to Services. Click on Add a service. Click on Nginx and click add.

Now click on + sign in mounted config files

A box will show. Now add what you see in the image below and press create

It is time to write in the nginx.conf file. Click on the edit file in the mounted config files part of the nginx service.

Now click on the nginx.conf file and add the below code
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 chat-appmjjg.eu-ccofhtfzmvsd.dopraxapp.com; #it should correspond to the one in deploy section for your app
        gzip on;
        gunzip on;
        gzip_types
            text/plain
            text/css
            text/js
            text/xml
            text/javascript
            application/javascript
            application/json
            application/xml
            application/rss+xml
            image/svg+xml;
      


           location / {
                proxy_pass http://main:8000;
                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;

            }

            location /static/ {
                alias /staticfiles/;
            }
           
            location ~^/ws/ {
                proxy_pass http://main:8001;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
                proxy_connect_timeout 70s;
                proxy_send_timeout 70s;
                proxy_read_timeout 70s;
            
    
            }

    }

}

So if you notice in the code where location / is defined, the requests are proxy passed to gunicorn i.e http://main:8000;

For location ~^/ws/, requests are proxy passed to daphne i.e http://main:8001;

So that is all. Head to the Deploy section and press the play Button.

Hurray, chat app is deployed and running!

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