Configuring ASGI Django Application using Daphne and Nginx Server
Background
My WSGI Django Application was already deployed using Gunicorn server and Nginx was used to serve static files and provide proxy to gunicorn. The task was to deploy ASGI Application present in the same project. All my WebSocket requests originated from /ws/ path.
Daphne
Daphne is a HTTP, HTTP2 and WebSocket protocol server for ASGI and ASGI-HTTP, developed to power Django Channels.
Let’s do it!
We know Daphne could be used to serve both WSGI and ASGI applications. However since my HTTP requests were already being served by gunicorn server, I decided to use Daphne to serve web sockets only. Note: this could be done because my WebSocket requests came from /ws/ path and hence the endpoints could be easily differentiated. (Also, since most people have deployment experience with gunicorn, it’s better to use it.)
asgi.py
import os import djangofrom channels.routing import get_default_applicationos.environ.setdefault(‘DJANGO_SETTINGS_MODULE’, ‘myproject.settings’)django.setup()application = get_default_application()
In order to run your asgi application, simply point Daphne to your ASGI application, and optionally set a bind address and port (defaults to localhost, port 8000):
daphne -b 0.0.0.0 -p 9001 myproject.asgi:application
In order to daemonize this process, I used systemd process supervisor. Inside /etc/systemd/system , I created a myproj-daphne.service file to run my process unobtrusively in the background.
myproj-daphne.service :
[Unit]
Description=myproject Daphne Service
After=network.target[Service]
Type=simple
User=yourusername # eg: User = ubuntu
WorkingDirectory=path_to_your_project # eg /home/ubuntu/myproject
ExecStart=daphne -p 9001 myproject.asgi:application
#edit myproject#If your daphne is not installed globally and is inside your #environment, enter the complete path to daphne
#ExecStart=/home/ubuntu/myproject/env/bin/daphne -p 9001
# myproject.asgi:application[Install]
WantedBy=multi-user.target
Now, my daphne server has been bound to port 9001 on the machine. I simply need to configure my nginx to proxy all WebSocket requests to this port. With the help of django-channels docs, I was able to configure my nginx file like this.
Nginx configuration
#added this block
upstream channels-backend {
server localhost:9001;
}server {
listen 80;
server_name mydomainlocation = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/ubuntu/myproject;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/myproject.sock;
}#path to proxy my WebSocket requests
location /ws/ {
proxy_pass http://channels-backend; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
proxy_redirect off;
proxy_set_header Host $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-Host $server_name;
}}
Whoohoo! All my WebSockets requests were successfully served by my Daphne server. Here’s a flowchat to make things clearer.
Note : Daphne was developed primarily to support Django channels ie it can handle HTTP / HTTP2 Protocols, WebSockets, Chat Protocols, IoT Protocols and more. However, for stability and performance it’s still recommended to use Gunicorn/uwsgi to serve HTTP requests and Daphne to handle the remaining requests.
Thank you for reading this article. Happy coding!