'What's the right procfile / requirements for heroku with django channels?

tl;dr - django channels app runs local with manage.py runserver but not on heroku.

I'm new to django channels - trying to deploy a very basic django app using channels to heroku. I initially built the project using the standard django polls tutorial and deployed that to heroku. Then i added in a chat app using the django channels tutorial. Managed to get that running fine locally using docker to run a redis server as they suggested and "python manage.py runserver".

I'm getting stuck trying to deploy this to heroku or run it locally using heroku local. I've already added the redis addon in heroku and modified settings.py to point to the REDIS_URL environment variable. I also modified my template to use wss if appropriate (I believe that's necessary for heroku):

var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
        var target = ws_scheme + '://'
        + window.location.host
        + '/ws/chat/'
        + roomName
        + '/';
        const chatSocket = new WebSocket(
          target
        );
...

Thus I'm concluding that the problem is with the procfile. I'm not sure what the instructions to use there are. The initial polls tutorial used:

web: gunicorn gettingstarted.wsgi --log-file -

If I just use that 'heroku local' works fine and deploying works fine, but when I try to send a chat message it does nothing and shows a 404 in the console. I know I have to change it to use an asgi server instead of gunicorn. found this tutorial on deploying an app with channels to heroku, which used:

web: daphne chat.asgi:channel_layer --port $PORT --bind 0.0.0.0 -v2
worker: python manage.py runworker -v2

I tried that, but that's where I get stuck. Here's what I'm getting when I run heroku local:

krishnas-air:python-getting-started Krishna$ heroku local
[OKAY] Loaded ENV .env File as KEY=VALUE Format
6:46:50 PM worker.1 |  Traceback (most recent call last):
6:46:50 PM worker.1 |    File "manage.py", line 8, in <module>
6:46:50 PM worker.1 |      from django.core.management import execute_from_command_line
6:46:50 PM worker.1 |  ImportError: No module named django.core.management
[DONE] Killing all processes with signal  SIGINT
6:46:50 PM worker.1 Exited with exit code null
6:46:50 PM web.1    |  Traceback (most recent call last):
6:46:50 PM web.1    |    File "/usr/local/bin/daphne", line 5, in <module>
6:46:50 PM web.1    |      from daphne.cli import CommandLineInterface
6:46:50 PM web.1    |    File "/usr/local/lib/python3.7/site-packages/daphne/cli.py", line 1, in <module>
6:46:50 PM web.1    |      import argparse
6:46:50 PM web.1    |    File "<frozen importlib._bootstrap>", line 983, in _find_and_load
6:46:50 PM web.1    |    File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
6:46:50 PM web.1    |    File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
6:46:50 PM web.1    |    File "<frozen importlib._bootstrap_external>", line 724, in exec_module
6:46:50 PM web.1    |    File "<frozen importlib._bootstrap_external>", line 857, in get_code
6:46:50 PM web.1    |    File "<frozen importlib._bootstrap_external>", line 525, in _compile_bytecode
6:46:50 PM web.1    |  KeyboardInterrupt
6:46:50 PM web.1    Exited with exit code null

The import error message made me think my requirements.txt could be missing something, so I've included that here as well for reference:

django
gunicorn
django-heroku
requests
channels
channels_redis
asgi_redis
asgiref
daphne
redis
gevent
gevent-websocket
greenlet

Thanks for any help!



Solution 1:[1]

I just figured out a very similar issue. First of all, although not Heroku specific, these docs are a must read.

I think the problem is that Heroku is routing ws:// requests through WSGI instead of ASGI. So the first step is to create an asgi.py file in the same folder as wsgi.py with something like this:

import django
from channels.routing import get_default_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dropdjango.settings")
django.setup()
application = get_default_application()

Then, in the Procfile, define dynos for web worker:

web: daphne <my-web-app>.asgi:application --port $PORT --bind 0.0.0.0 -v2
worker: python manage.py runworker channel_layer -v2

If the dynos don't exist yet in Heroku, create them using the Heroku CLI. In my case, the web dyno already existed so I only created the worker dyno:

heroku ps:scale worker=1:free -a <your-heroku-app-name>

Finally, double-check your settings.py to make sure you have:

ASGI_APPLICATION="<my-web-app>.routing.application"
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {"hosts": [os.environ.get("REDIS_URL", "redis://localhost:6379")]},
    },
}

Warning: This will run all your requests through daphne. I've read of caveats but I haven't experienced them yet.

Solution 2:[2]

I just want to say this thread is a mess. I will figure out the correct configuration of everything and post back here my results.

I'll make a github repo to share all the files.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ivan Figueroa
Solution 2 Abstract Space Crack