Django Channels works locally (InMemoryStore) but not on herokuapp (RedisStore), and simply retries connecting forever.

So my question is:

Has anyone been successful in using Django site + Django channels + Heroku hosting approach for a chat app on their site?

If so, then please post your relevant codes. Thanks.

Here is the live site chat page:

Chat Index (join a room => broken state => this quesiton)

So I am going to show you some code real quick:

asgi.py:

import os

import django
from channels.auth import AuthMiddlewareStack  # new import
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application

asgi_app = get_asgi_application()

import chat.routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jobs.settings')

application = ProtocolTypeRouter({
  'http': asgi_app,
  'websocket': AuthMiddlewareStack(  # new
        URLRouter(
            chat.routing.websocket_urlpatterns
        )
    ),  # new
})

Procfile (a heroku thing):

web: gunicorn jobs.wsgi --log-file -
worker: python manage.py runworker channels --settings=jobs.settings -v2

chat/urls.py:

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index_view, name='chat-index'),
    path('<str:room_name>/', views.room_view, name='chat-room'),
]

chat/views.py:

from django.shortcuts import render
import os
from chat.models import Room


def index_view(request):
    return render(request, 'chat/index.html', {
        'rooms': Room.objects.all(),
    })

def room_view(request, room_name):
    chat_room, created = Room.objects.get_or_create(name=room_name)
    return render(request, 'chat/room.html', {
        'room': chat_room,
    })

chat/routing.py:

from django.urls import re_path

import chat.consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<room_name>\w+)/$', chat.consumers.ChatConsumer.as_asgi()),
]

chat/models.py:

from accounts.models import User
from django.db import models

class Room(models.Model):
    name = models.CharField(max_length=128)
    online = models.ManyToManyField(to=User, blank=True)

    def get_online_count(self):
        return self.online.count()

    def join(self, user):
        self.online.add(user)
        self.save()

    def leave(self, user):
        self.online.remove(user)
        self.save()

    def __str__(self):
        return f'{self.name} ({self.get_online_count()})'

class Message(models.Model):
    user = models.ForeignKey(to=User, on_delete=models.CASCADE)
    room = models.ForeignKey(to=Room, on_delete=models.CASCADE)
    content = models.CharField(max_length=512)
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f'{self.user.username}: {self.content} [{self.timestamp}]'

chat/consumers.py:

import json

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer

from .models import Room, Message

class ChatConsumer(WebsocketConsumer):

    def __init__(self, *args, **kwargs):
        super().__init__(args, kwargs)
        self.room_name = None
        self.room_group_name = None
        self.room = None
        self.user = None
        self.user_inbox = None

    def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = f'chat_{self.room_name}'
        self.room = Room.objects.get(name=self.room_name)
        self.user = self.scope['user']
        self.user_inbox = f'inbox_{self.user.email.replace("@", "_")}'

        # connection has to be accepted
        self.accept()

        # join the room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name,
        )

        users = [user.email for user in self.room.online.all()]
        
        # send the user list to the newly joined user
        self.send(json.dumps({
            'type': 'user_list',
            'users': users,
        }))

        if self.user.is_authenticated:
            # create a user inbox for private messages
            async_to_sync(self.channel_layer.group_add)(
                self.user_inbox,
                self.channel_name,
            )

            # send the join event to the room
            async_to_sync(self.channel_layer.group_send)(
                self.room_group_name,
                {
                    'type': 'user_join',
                    'user': self.user.email,
                }
            )
            self.room.online.add(self.user)

    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name,
        )

        if self.user.is_authenticated:
            # delete the user inbox for private messages
            async_to_sync(self.channel_layer.group_add)(
                self.user_inbox,
                self.channel_name,
            )

            # send the leave event to the room
            async_to_sync(self.channel_layer.group_send)(
                self.room_group_name,
                {
                    'type': 'user_leave',
                    'user': self.user.email,
                }
            )
            self.room.online.remove(self.user)

    def receive(self, text_data=None, bytes_data=None):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        if not self.user.is_authenticated:
            return
        if message.startswith('/pm '):
            split = message.split(' ', 2)
            target = split[1]
            target_msg = split[2]

            # send private message to the target
            async_to_sync(self.channel_layer.group_send)(
                f'inbox_{target}',
                {
                    'type': 'private_message',
                    'user': self.user.email,
                    'message': target_msg,
                }
            )
            # send private message delivered to the user
            self.send(json.dumps({
                'type': 'private_message_delivered',
                'target': target,
                'message': target_msg,
            }))
            return

        # send chat message event to the room
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
                'type': 'chat_message',
                'user': self.user.email,
                'message': message,
            }
        )
        Message.objects.create(user=self.user, room=self.room, content=message)

    def chat_message(self, event):
        self.send(text_data=json.dumps(event))

    def user_join(self, event):
        self.send(text_data=json.dumps(event))

    def user_leave(self, event):
        self.send(text_data=json.dumps(event))

    def private_message(self, event):
        self.send(text_data=json.dumps(event))

    def private_message_delivered(self, event):
        self.send(text_data=json.dumps(event))

Errors in the browser dev console:

room.js:1 Sanity check from room.js.
DevTools failed to load source map: Could not load content for chrome-extension://mooikfkahbdckldjjndioackbalphokd/assets/atoms.js.map: System error: net::ERR_BLOCKED_BY_CLIENT
DevTools failed to load source map: Could not load content for chrome-extension://mooikfkahbdckldjjndioackbalphokd/assets/polyfills.js.map: System error: net::ERR_BLOCKED_BY_CLIENT
DevTools failed to load source map: Could not load content for chrome-extension://mooikfkahbdckldjjndioackbalphokd/assets/escape.js.map: System error: net::ERR_BLOCKED_BY_CLIENT
DevTools failed to load source map: Could not load content for chrome-extension://mooikfkahbdckldjjndioackbalphokd/assets/playback.js.map: System error: net::ERR_BLOCKED_BY_CLIENT
DevTools failed to load source map: Could not load content for chrome-extension://mooikfkahbdckldjjndioackbalphokd/assets/record.js.map: System error: net::ERR_BLOCKED_BY_CLIENT
content_start.js:137 Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-mc9LH2e3kV1BBF1B3icEq0dR656rD3pdHToRTsWEQTI='), or a nonce ('nonce-...') is required to enable inline execution.

syLt @ content_start.js:137
BEWy @ content_start.js:123
YUWZ @ content_start.js:95
jOSM @ content_start.js:155
mKFS @ content_start.js:238
cYUN @ content_start.js:190
Sypg @ content_start.js:212
TxKY @ content_start.js:253
(anonymous) @ content_start.js:259
room.js:48 WebSocket connection to 'wss://fairworkcycle.herokuapp.com/ws/chat/test/' failed: 
connect @ room.js:48
(anonymous) @ room.js:104
room.js:99 WebSocket encountered an error: undefined
room.js:100 Closing the socket.
room.js:55 WebSocket connection closed unexpectedly. Trying to reconnect in 2s...
room.js:57 Reconnecting...
room.js:48 WebSocket connection to 'wss://fairworkcycle.herokuapp.com/ws/chat/test/' failed: 
connect @ room.js:48
(anonymous) @ room.js:58
room.js:99 WebSocket encountered an error: undefined
room.js:100 Closing the socket.
room.js:55 WebSocket connection closed unexpectedly. Trying to reconnect in 2s...
room.js:57 Reconnecting...
room.js:48 WebSocket connection to 'wss://fairworkcycle.herokuapp.com/ws/chat/test/' failed: 
connect @ room.js:48
(anonymous) @ room.js:58
room.js:99 WebSocket encountered an error: undefined
room.js:100 Closing the socket.
room.js:55 WebSocket connection closed unexpectedly. Trying to reconnect in 2s...
room.js:57 Reconnecting...
room.js:48 WebSocket connection to 'wss://fairworkcycle.herokuapp.com/ws/chat/test/' failed: 
connect @ room.js:48
(anonymous) @ room.js:58
room.js:99 WebSocket encountered an error: undefined
room.js:100 Closing the socket.
room.js:55 WebSocket connection closed unexpectedly. Trying to reconnect in 2s...
room.js:57 Reconnecting...

Errors on the heroku logs --tail side:

2022-05-09T00:30:42.032857+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=e397b517-6063-4f90-bd03-f08eadb9ceef fwd="47.215.230.184" dyno=web.1 connect=0ms service=8ms status=404 bytes=3622 protocol=https
2022-05-09T00:30:45.002477+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=f7bc7490-c132-4e6f-9808-7ce3d0924c88 fwd="47.215.230.184" dyno=web.1 connect=0ms service=6ms status=404 bytes=3622 protocol=https
2022-05-09T00:30:45.001183+00:00 app[web.1]: 10.1.3.205 - - [09/May/2022:00:30:44 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:30:48.041544+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=c49ace87-bd89-4862-aff1-52cb31eda980 fwd="47.215.230.184" dyno=web.1 connect=0ms service=6ms status=404 bytes=3622 protocol=https
2022-05-09T00:30:48.040336+00:00 app[web.1]: 10.1.90.231 - - [09/May/2022:00:30:48 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:30:51.024792+00:00 app[web.1]: 10.1.16.74 - - [09/May/2022:00:30:51 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:30:51.024631+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=38938a87-f83b-4ce4-82cb-785d4eed33c5 fwd="47.215.230.184" dyno=web.1 connect=0ms service=10ms status=404 bytes=3622 protocol=https
2022-05-09T00:30:54.046057+00:00 app[web.1]: 10.1.22.44 - - [09/May/2022:00:30:54 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:30:54.047688+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=b3097957-73f6-4d17-9532-c84ba363a30c fwd="47.215.230.184" dyno=web.1 connect=0ms service=6ms status=404 bytes=3622 protocol=https
2022-05-09T00:30:57.069357+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=abfe6f78-5bbf-47e2-b956-7307db3cf5d1 fwd="47.215.230.184" dyno=web.1 connect=0ms service=7ms status=404 bytes=3622 protocol=https
2022-05-09T00:30:57.068065+00:00 app[web.1]: 10.1.26.127 - - [09/May/2022:00:30:57 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:31:00.065090+00:00 app[web.1]: 10.1.22.44 - - [09/May/2022:00:31:00 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:31:00.066342+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=05545a7c-b356-43af-ae66-499d1648fa74 fwd="47.215.230.184" dyno=web.1 connect=0ms service=10ms status=404 bytes=3622 protocol=https
2022-05-09T00:31:03.078298+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=50c66355-ce3b-4b1a-ae80-408f31a1e098 fwd="47.215.230.184" dyno=web.1 connect=0ms service=5ms status=404 bytes=3622 protocol=https
2022-05-09T00:31:03.077381+00:00 app[web.1]: 10.1.25.200 - - [09/May/2022:00:31:03 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:31:06.083485+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=d0fc6c58-49b8-4682-b660-ef8b06db3f95 fwd="47.215.230.184" dyno=web.1 connect=0ms service=11ms status=404 bytes=3622 protocol=https
2022-05-09T00:31:06.082592+00:00 app[web.1]: 10.1.32.167 - - [09/May/2022:00:31:06 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:31:09.130023+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=0b1bcc2d-6cf9-4705-86f4-f338ecf1995d fwd="47.215.230.184" dyno=web.1 connect=0ms service=16ms status=404 bytes=3622 protocol=https
2022-05-09T00:31:09.129169+00:00 app[web.1]: 10.1.25.200 - - [09/May/2022:00:31:09 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:31:12.113146+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=584ec6bf-2403-49b8-b6fa-5f39652fa160 fwd="47.215.230.184" dyno=web.1 connect=0ms service=6ms status=404 bytes=3622 protocol=https
2022-05-09T00:31:12.111819+00:00 app[web.1]: 10.1.93.52 - - [09/May/2022:00:31:12 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:31:15.287083+00:00 app[web.1]: 10.1.32.167 - - [09/May/2022:00:31:15 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
2022-05-09T00:31:15.288142+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=8e8a0019-728b-4bd9-b87b-4980e0769459 fwd="47.215.230.184" dyno=web.1 connect=0ms service=4ms status=404 bytes=3622 protocol=https
2022-05-09T00:31:18.372489+00:00 heroku[router]: at=info method=GET path="/ws/chat/test/" host=fairworkcycle.herokuapp.com request_id=dd5962a9-85b9-40e1-9275-59f04af63b98 fwd="47.215.230.184" dyno=web.1 connect=0ms service=4ms status=404 bytes=3622 protocol=https
2022-05-09T00:31:18.371198+00:00 app[web.1]: 10.1.43.209 - - [09/May/2022:00:31:18 +0000] "GET /ws/chat/test/ HTTP/1.1" 404 3256 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"

These both go on indefinitely (it’s trying to make a connection, but cant). That’s with the Redis addon installed onto fairworkcycle.herokuapp.com. However, with InMemoryStore on local machine, everything works great.


Here’s the relevant part of settings.py:

# Chat settings
ASGI_APPLICATION = "jobs.asgi.application"

if os.environ.get('LIVE_SITE', 1) == '0':    
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "channels.layers.InMemoryChannelLayer"
        }
    }
else:
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "asgi_redis.RedisChannelLayer",
            "CONFIG": {
                "hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')],
            },
            "ROUTING": "chat.routing.channel_routing",
        },
    }    

The Frontend

chat/room.html:

{% load static %}

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>django-channels-chat</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
        <style>
            #chatLog {
                height: 300px;
                background-color: #FFFFFF;
                resize: none;
            }

            #onlineUsersSelector {
                height: 300px;
            }
        </style>
    </head>
    <body>
        <div class="container mt-3 p-5">
            <h2>django-channels-chat</h2>
            <div class="row">
                <div class="col-12 col-md-8">
                    <div class="mb-2">
                        <label for="chatLog">Room: #{{ room.name }}</label>
                        <textarea class="form-control" id="chatLog" readonly></textarea>
                    </div>
                    <div class="input-group">
                        <input type="text" class="form-control" id="chatMessageInput" placeholder="Enter your chat message">
                        <div class="input-group-append">
                            <button class="btn btn-success" id="chatMessageSend" type="button">Send</button>
                        </div>
                    </div>
                </div>
                <div class="col-12 col-md-4">
                    <label for="onlineUsers">Online users</label>
                    <select multiple class="form-control" id="onlineUsersSelector">
                    </select>
                </div>
            </div>
            {{ room.name|json_script:"roomName" }}
        </div>
        <script src="{% static 'js/chat/room.js' %}"></script>
    </body>
</html>

js/chat/room.js

console.log("Sanity check from room.js.");

const roomName = JSON.parse(document.getElementById('roomName').textContent);

let chatLog = document.querySelector("#chatLog");
let chatMessageInput = document.querySelector("#chatMessageInput");
let chatMessageSend = document.querySelector("#chatMessageSend");
let onlineUsersSelector = document.querySelector("#onlineUsersSelector");

// adds a new option to 'onlineUsersSelector'
function onlineUsersSelectorAdd(value) {
    if (document.querySelector("option[value='" + value + "']")) return;
    let newOption = document.createElement("option");
    newOption.value = value;
    newOption.innerHTML = value;
    onlineUsersSelector.appendChild(newOption);
}

// removes an option from 'onlineUsersSelector'
function onlineUsersSelectorRemove(value) {
    let oldOption = document.querySelector("option[value='" + value + "']");
    if (oldOption !== null) oldOption.remove();
}

// focus 'chatMessageInput' when user opens the page
chatMessageInput.focus();

// submit if the user presses the enter key
chatMessageInput.onkeyup = function(e) {
    if (e.keyCode === 13) {  // enter key
        chatMessageSend.click();
    }
};

// clear the 'chatMessageInput' and forward the message
chatMessageSend.onclick = function() {
    if (chatMessageInput.value.length === 0) return;
    chatSocket.send(JSON.stringify({
        "message": chatMessageInput.value,
    }));
    chatMessageInput.value = "";
};

let chatSocket = null;

function connect() {
    let protocol = (window.location.protocol === 'https:' ? 'wss' : 'ws') + '://';
    chatSocket = new WebSocket(protocol + window.location.host + "/ws/chat/" + roomName + "/");

    chatSocket.onopen = function(e) {
        console.log("Successfully connected to the WebSocket.");
    }

    chatSocket.onclose = function(e) {
        console.log("WebSocket connection closed unexpectedly. Trying to reconnect in 2s...");
        setTimeout(function() {
            console.log("Reconnecting...");
            connect();
        }, 2000);
    };

    chatSocket.onmessage = function(e) {
        const data = JSON.parse(e.data);
        console.log(data);

        switch (data.type) {
            case "chat_message":
                chatLog.value += data.user + ": " + data.message + "\n";
                break;
            case "user_list":
                for (let i = 0; i < data.users.length; i++) {
                    onlineUsersSelectorAdd(data.users[i]);
                }
                break;
            case "user_join":
                chatLog.value += data.user + " joined the room.\n";
                onlineUsersSelectorAdd(data.user);
                break;
            case "user_leave":
                chatLog.value += data.user + " left the room.\n";
                onlineUsersSelectorRemove(data.user);
                break;
            case "private_message":
                chatLog.value += "PM from " + data.user + ": " + data.message + "\n";
                break;
            case "private_message_delivered":
                chatLog.value += "PM to " + data.target + ": " + data.message + "\n";
                break;
            default:
                console.error("Unknown message type!");
                break;
        }

        // scroll 'chatLog' to the bottom
        chatLog.scrollTop = chatLog.scrollHeight;
    };

    chatSocket.onerror = function(err) {
        console.log("WebSocket encountered an error: " + err.message);
        console.log("Closing the socket.");
        chatSocket.close();
    }
}
connect();

onlineUsersSelector.onchange = function() {
    chatMessageInput.value = "/pm " + onlineUsersSelector.value + " ";
    onlineUsersSelector.value = null;
    chatMessageInput.focus();
};

What I’ve tried:

About 10 different solutions from Stack overflow. SO got me this far though, so thought it would help me cross this hurdle. I’ve googled for both the frontend and backend error strings.

Next thing I’m going to try is the REDIS TLS environment variable that also auto-appeared on Heroku dashboard after I installed the Redis addon.

What webserver is being used? How is it configured?

Based on this message:
WebSocket connection to 'wss://fairworkcycle.herokuapp.com/ws/chat/test/' failed:

very superficially, it appears as if the web server may not be “upgrading” the https connection to wss.

If I’m correct, this would mean it’s not even getting through to your consumer.

I don’t really know anything about Heroku, but I don’t see how you’re set up to run any asgi service such as daphne or uvicorn to handle the websockets.

1 Like

web: gunicorn jobs.wsgi --log-file -
web2: daphne jobs.asgi:application --port 8001 --bind localhost -v2
worker: python manage.py runworker channels --settings=jobs.settings -v2

Is my current Procfile and the above results in the same error. I’ve tried about 50 different configurations of everything today and the buid time is slow on heroku and locally so…

I found this:

Running with HTTPS¶
To run uvicorn with https, a certificate and a private key are required. The recommended way to get them is using Let’s Encrypt.

For local development with https, it’s possible to use mkcert to generate a valid certificate and private key.

$ uvicorn example:app --port 5000 --ssl-keyfile=./key.pem --ssl-certfile=./cert.pem
Running gunicorn worker¶
It also possible to use certificates with uvicorn’s worker for gunicorn

$ gunicorn --keyfile=./key.pem --certfile=./cert.pem -k uvicorn.workers.UvicornWorker ex

But Herokuapps are already “on https”. So I’m not sure if I need all of that certificate setup. What do you think?

I tried this heroku run "daphne jobs.asgi:application --port 8001 --bind 0.0.0.0 -v2". And it does pop up an asgi server and it stays running. However when I put the same line inside of a web: or web2: dyno in the Procfile, and I do heroku ps:restart --app=fairworkcycle and then heroku ps --app=fairworkcycle, it is listed as crashed. I also think I have to under the Heroku online Dashboard > App: fairworkcycle > Resources make sure each dyno (and there is a maximum of 2 free dynos allowed; and I have 3 to run), is toggled on.

So I am still having a connectivity problem, but at least I have more information on it. I also need to upgrade my dynos to “hobby” level at $7 / dyno on heroku.

You don’t need to use both gunicorn and daphne. Daphne alone is capable of serving both http and websocket connections.

If you do run daphne on port 8001 along with gunicorn, then you would need to ensure that the wss connections are made on port 8001 and not your regular server port - or you would need a web server (e.g. nginx) to direct the traffic once it has been received.

See the docs at Deploying — Channels 3.0.4 documentation

1 Like

That depends upon what is responsible for being the ssl endpoint. If heroku is the endpoint, then no, you don’t need to do anything like that.

1 Like

@KenWhitesell How would you do that in Django, there being only one ASGI_APPLICATION setting in settings.py?

The Routing docs show an asgi.py file that routes both http requests and websocket connections.

@KenWhitesell I think I’m seeing now how to code the Procfile using one line and daphne. However, it’s not working. I copied a channels based chat app example and it’s not sending, so I am onto the next bug. The above was with another example of channels I was trying out. So maybe by the time I’m through debugging this example, it’ll be working. Who’d have thought it would be so difficult to install and customize a chat plugin to your Django site…

1 Like

Yess even iam also getting same message sent in development server but not sending in heroku deploy server… Anyone help me out.