Django Daphne not working with WSS, Ngnix and Docker
I'm trying to deploy an application built with Django and Daphne to open a WebSocket connection. Everything works perfectly on localhost, as I'm not using Nginx.
However, in production, I need to use Nginx to serve SSL/HTTPS connections, including WSS. Unfortunately, it no longer works. When I try to use HTTPS on port 443, the WebSocket connection remains pending indefinitely until it eventually fails.
I really considered removing Nginx because I found a Daphne configuration that runs on port 443 with an SSL certificate.
daphne -b 0.0.0.0 -e ssl:443:privateKey=/privkey.pem:certKey=/fullchain.pem core.asgi:application
However, I would still need Nginx to serve static files.
I feel like I've tried everything so far. Can someone help me?
docker-compose.yml
services:
web:
build: .
container_name: meetlink_django_app
ports:
- 8000:8000
- 8001:8001
volumes:
- .:/app
- static_volume:/app/core/staticfiles
- ./certbot/conf:/app/certbot/conf:ro
- ./certbot/www:/app/certbot/www:ro
environment:
- DEBUG=True
- PYTHONUNBUFFERED=1
- DJANGO_SETTINGS_MODULE=core.settings
- ENV=${ENV}
networks:
- meetlink-network
command:
- bash
- -c
- "python manage.py migrate && python manage.py runserver 0.0.0.0:8000 & daphne -b 0.0.0.0 -p 8001 core.asgi:application"
depends_on:
database:
condition: service_healthy
cache:
condition: service_healthy
database:
image: postgres
container_name: meetlink_django_database
environment:
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_NAME}
ports:
- ${DB_PORT}:5432
volumes:
- database_volume:/var/lib/postgresql/data
networks:
- meetlink-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
interval: 5s
timeout: 10s
retries: 5
cache:
image: redis
container_name: meetlink_django_cache
ports:
- ${CACHE_PORT}:6379
networks:
- meetlink-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 10s
retries: 5
server:
image: nginx
container_name: meetlink_django_server
ports:
- ${SSL_SERVER_PORT}:443
- ${SERVER_PORT}:80
depends_on:
- web
networks:
- meetlink-network
volumes:
- ./.nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- static_volume:/var/www/html/static
- ./certbot/www/:/var/www/certbot:ro
- ./certbot/conf/:/var/www/conf:ro
certbot:
image: certbot/certbot:latest
volumes:
- ./certbot/www/:/var/www/certbot/:rw
- ./certbot/conf/:/etc/letsencrypt/:rw
network_mode: host
volumes:
static_volume:
database_volume:
networks:
meetlink-network:
driver: bridge
.nginx/default.conf
server {
listen 80;
listen [::]:80;
server_name totemvirtual.com.br www.totemvirtual.com.br;
tcp_nodelay on;
location / {
proxy_pass http://meetlink_django_app:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_buffering off;
}
location /ws/ {
proxy_pass http://meetlink_django_app:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_buffering off;
}
location /static/ {
autoindex on;
alias /var/www/html/static/;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name totemvirtual.com.br www.totemvirtual.com.br;
ssl_certificate /var/www/conf/live/totemvirtual.com.br/fullchain.pem;
ssl_certificate_key /var/www/conf/live/totemvirtual.com.br/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
proxy_connect_timeout 43200000;
tcp_nodelay on;
location / {
proxy_pass http://meetlink_django_app:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_buffering off;
}
location /static/ {
autoindex on;
alias /var/www/html/static/;
}
}
core/asgi.py
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import re_path
from meetlink.domain.call.call_consumer import CallConsumer
from meetlink.domain.meeting.meeting_consumer import MeetingConsumer
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
application = ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
[
re_path("ws/calls/?$", CallConsumer.as_asgi()),
re_path("ws/meetings/?$", MeetingConsumer.as_asgi()),
]
)
),
}
)
core/settings.py | channel layers config
CHANNEL_LAYERS = {
"default": {
"BACKEND": config("CHANNELS_CACHE_ENGINE"),
"CONFIG": {
"hosts": [(config("CACHE_HOST"), config("CACHE_PORT"))],
"prefix": "meetlink.channels",
},
},
}
Dockerfile (I think it's make no difference)
FROM python:3.13-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpq-dev \
build-essential \
python3-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN pip install --no-cache-dir pipenv
COPY Pipfile Pipfile.lock /app/
RUN pipenv install --system --deploy
COPY . /app/
WORKDIR /app/core/
COPY start.sh /app/start.sh
RUN python manage.py collectstatic --noinput --clear
EXPOSE 8000 8001 443 80