22

I have a django project and recently added channels to use websockets. This seems to all work fine, but the problem I have is to get the production ready.

My setup is as follows:

Nginx web server
Gunicorn for django
SSL enabled

Since I have added channels to the mix. I have spent the last day trying to get it to work.

On all the turtotials they say you run daphne on some port then show how to setup nginx for that.

But what about having gunicorn serving django?

So now I have guncorn running this django app on 8001

If I run daphne on another port, lets say 8002 - how should it know its par of this django project? And what about run workers?

Should Gunicorn, Daphne and runworkers all run together?

lapinkoira
  • 8,320
  • 9
  • 51
  • 94
Harry
  • 13,091
  • 29
  • 107
  • 167
  • I am not sure if you really need both? I have not worked with daphne so I may be wrong. Please check if this thread of any help https://stackoverflow.com/questions/42417919/deploy-to-docker-with-nginx-django-daphne – Tarun Lalwani Sep 12 '17 at 14:16

3 Answers3

26

This question is actually addressed in the latest Django Channels docs:

It is good practice to use a common path prefix like /ws/ to distinguish WebSocket connections from ordinary HTTP connections because it will make deploying Channels to a production environment in certain configurations easier.

In particular for large sites it will be possible to configure a production-grade HTTP server like nginx to route requests based on path to either (1) a production-grade WSGI server like Gunicorn+Django for ordinary HTTP requests or (2) a production-grade ASGI server like Daphne+Channels for WebSocket requests.

Note that for smaller sites you can use a simpler deployment strategy where Daphne serves all requests - HTTP and WebSocket - rather than having a separate WSGI server. In this deployment configuration no common path prefix like is /ws/ is necessary.

In practice, your NGINX configuration would then look something like (shortened to only include relevant bits):

upstream daphne_server {
  server unix:/var/www/html/env/run/daphne.sock fail_timeout=0;
}

upstream gunicorn_server {
  server unix:/var/www/html/env/run/gunicorn.sock fail_timeout=0;
}

server { 
  listen   80; 
  server_name _;

  location /ws/ {
    proxy_pass http://daphne_server;
  }

  location / {
    proxy_pass http://gunicorn_server;
  }
}

(Above it is assumed that you are binding the Gunicorn and Daphne servers to Unix socket files.)

Kal
  • 1,176
  • 19
  • 22
  • if I understand good, gunicorn replaces the worker? so it is not necessary to run `python manage.py runworker` ? –  Jun 09 '20 at 14:51
  • after making some tests no, it is not necessary to use 'runworker'. –  Jun 09 '20 at 16:23
  • 1
    `python manage.py runworker` is only required in Django Channels 2.x if you want to use channels to run worker or background processes (https://channels.readthedocs.io/en/latest/topics/worker.html). Usually, you'll use something like celery for that, though (they mention why that is in the doc link included). – Kal Jun 09 '20 at 18:24
  • is this the same in Channels 3.0.3? – Abhijith Konnayil May 05 '21 at 11:30
  • I just took a look at the v3 release notes and it doesn't look like it should have any impact on deployment. The official deployment guide included in the docs (https://channels.readthedocs.io/en/stable/deploying.html#configuring-the-asgi-application) also suggests that the above solution should still hold. If you find otherwise, either add your answer here or let me know so I can investigate and update my answer. – Kal May 05 '21 at 15:48
7

I have created an example how to mix Django Channels and Django Rest Framework. I set nginx routing that:

  • websockets connections are going to daphne server
  • HTTP connections (REST API) are going to gunicorn server

Here is my nginx configuration file:

upstream app {
    server wsgiserver:8000;
}

upstream ws_server {
    server asgiserver:9000;
}


server {
    listen 8000 default_server;
    listen [::]:8000;

    client_max_body_size 20M;

    location / {
        try_files $uri @proxy_to_app;
    }

    location /tasks {
        try_files $uri @proxy_to_ws;
    }

    location @proxy_to_ws {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;

        proxy_pass   http://ws_server;
    }

    location @proxy_to_app {
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Url-Scheme $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;

        proxy_pass   http://app;
    }

}
pplonski
  • 5,023
  • 1
  • 30
  • 34
2

I recently answered a similiar question, have a look there for an explanation on how django channels work.

Basically, you don't need gunicorn anymore. You have daphne which is the interface server that accepts HTTP/Websockets and you have your workers that run django views. Then obviously you have your channel backend that glues everything together.

To make it work you have to configure CHANNEL_LAYERS in settings.py and also run the interface server:

$ daphne my_project.asgi:channel_layer

and your worker:

$ python manage.py runworker

NB! If you chose redis as the channel backend, pay attention to file sizes you're serving. If you have large static files make sure NGINX serves them or otherwise clients will experience cryptic errors that may occur due to redis running out of memory.

roman
  • 525
  • 4
  • 17
  • I will not recommend daphne to use as WSGI for HTTP because it requires more resource than a gunicorn worker receiving HTTP traffic – Dean Christian Armada Mar 09 '18 at 07:00
  • it is not reocmmended to use daphne in production –  Jun 09 '20 at 15:34
  • 2
    Who recommends against Daphne in production? Not the official Django-channels docs. https://channels.readthedocs.io/en/stable/deploying.html – Markus Feb 13 '21 at 13:04