Back to all posts
Tutorial 46 views 9 this week

Deploying Django WebSockets with Nginx and Gunicorn: A Complete Troubleshooting Guide

K
Kokou Elvis Khorem Blitti
Published on July 15, 2025

When deploying Django applications with WebSocket support to production, you'll quickly discover that the local development setup doesn't translate directly to a production environment. This guide walks through the common issues encountered when deploying Django WebSockets with Nginx and Gunicorn, and provides practical solutions.

The Problem: WebSockets Don't Work with Gunicorn

The first major hurdle is understanding that Gunicorn cannot handle WebSocket connections. While Gunicorn excels at serving HTTP requests, WebSockets require a different protocol that Gunicorn simply doesn't support.

Error Symptoms:
- WebSocket connections fail with "connection refused" errors
- Frontend shows connection attempts but they never establish
- Works perfectly in local development but fails in production

 

The Solution: Dual Server Architecture

The solution involves running two separate servers:
1. Gunicorn for HTTP requests (your existing setup)
2. Daphne (or Uvicorn) for WebSocket connections

 

Step-by-Step Implementation

 

1. Install Required Packages

```bash
pip install daphne channels-redis
```

 

2. Configure Django Settings

Update your `settings.py`:

```python
# Enable channels
INSTALLED_APPS = [
    # ... your existing apps
    'channels',
]

# Point to your ASGI application
ASGI_APPLICATION = 'your_project.asgi.application'

# Configure channel layers (Redis for production)
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}

```

 

3. Create/Fix Your ASGI Configuration

The most critical part is getting your `asgi.py` file right. Here's the correct structure:

```python
import os
import django
from django.core.asgi import get_asgi_application

# Set Django settings module
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings')

# Setup Django BEFORE importing anything else
django.setup()

# Now import Django-related modules
from channels.routing import ProtocolTypeRouter, URLRouter
from .middleware import JWTAuthMiddleware  # Your custom middleware
from your_app.routing import websocket_urlpatterns

# Get the Django ASGI application
django_asgi_app = get_asgi_application()

# Create the protocol router
application = ProtocolTypeRouter({
    "http": django_asgi_app,
    "websocket": JWTAuthMiddleware(
        URLRouter(websocket_urlpatterns)
    ),
})

```

Critical Point: The `django.setup()` call must happen before importing any Django models or apps. This prevents the "Apps aren't loaded yet" error.

 

4. Configure Nginx for Dual Servers

Update your Nginx configuration to route HTTP and WebSocket traffic to different servers:

```nginx
server {
    listen 80;
    server_name your_domain_or_ip;
    
    location = /favicon.ico { access_log off; log_not_found off; }
    
    location /static/ {
        root /path/to/your/app;
    }
    
    location /media/ {
        root /path/to/your/app;
    }
    
    # NEW: Handle WebSocket requests with Daphne
    location /ws/ {
        proxy_pass http://127.0.0.1:8001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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-Proto $scheme;
        proxy_read_timeout 86400;
    }
    
    # Keep existing HTTP requests with Gunicorn
    location / {
        include proxy_params;
        proxy_pass http://unix:/path/to/your/gunicorn.sock;
        proxy_read_timeout 1000;
        proxy_connect_timeout 600;
        client_max_body_size 100M;
    }
}
```

 

5. Create Systemd Service for Daphne

Create `/etc/systemd/system/daphne.service`:

```ini
[Unit]
Description=Daphne ASGI Server
After=network.target

[Service]
Type=simple
User=your_user
Group=your_group
WorkingDirectory=/path/to/your/app
ExecStart=/path/to/your/venv/bin/daphne -b 127.0.0.1 -p 8001 your_project.asgi:application
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
```

 

6. Install and Configure Redis

```bash
sudo apt update
sudo apt install redis-server
sudo systemctl enable redis
sudo systemctl start redis
```

 

Common Issues and Solutions

 

Issue 1: "Apps aren't loaded yet" Error

Problem: Django models are imported before Django is properly initialized.

Solution: Ensure `django.setup()` is called before any Django imports in your `asgi.py`.

 

Issue 2: WebSocket Connects But No Messages

Problem: Often occurs with `InMemoryChannelLayer` in production.

Solution: Switch to Redis-based channel layer:

```python
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}
```

 

Issue 3: Circular Import Problems

Problem: `asgi.py` imports from `routing.py` which imports Django models.

Solution: Consolidate your ASGI configuration into a single file with proper import order.

 

Issue 4: Wrong ASGI_APPLICATION Setting

Problem: Settings still point to old routing file.

Solution: Update `settings.py`:

```python
# Change from:
ASGI_APPLICATION = 'your_project.routing.application'

# To:
ASGI_APPLICATION = 'your_project.asgi.application'
```

 

Issue 5: Nginx Can't Connect to Daphne

Problem: Daphne service isn't running or listening on the wrong port.

Solution: 
- Check service status: `sudo systemctl status daphne`
- Verify port binding: `sudo netstat -tlnp | grep 8001`
- Check logs: `sudo journalctl -u daphne -f`

 

Testing Your Setup

 

1. Test ASGI Application Loading

```bash
cd /path/to/your/app
source venv/bin/activate
python -c "from your_project.asgi import application; print('ASGI application loaded successfully')"
```

 

2. Test Daphne Manually

```bash
daphne -b 127.0.0.1 -p 8001 your_project.asgi:application
```

 

3. Test WebSocket Connection

```bash
# Install wscat
npm install -g wscat

# Test connection
wscat -c "ws://your_domain/ws/your_endpoint/"
```

 

Best Practices for Production

1. Use Redis for Channel Layers: InMemoryChannelLayer is fine for development but use Redis in production.

 

2. Implement Proper Authentication: Use custom middleware for WebSocket authentication (like JWT).

 

3. Monitor Both Services: Set up monitoring for both Gunicorn and Daphne services.

 

4. Use Process Managers: Consider using supervisord or systemd to manage your services.

 

5. Enable Logging: Add comprehensive logging to debug WebSocket issues:

```python
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/websocket.log',
        },
    },
    'loggers': {
        'your_app': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}
```

 

Final Service Management

```bash
# Enable and start all services
sudo systemctl enable daphne
sudo systemctl enable redis
sudo systemctl start daphne
sudo systemctl start redis
sudo systemctl reload nginx

# Check status
sudo systemctl status daphne
sudo systemctl status redis
sudo systemctl status nginx
```

 

Conclusion

Deploying Django WebSockets in production requires a different architecture than simple HTTP applications. The key insights are:

1. Gunicorn cannot handle WebSockets - you need an ASGI server like Daphne
2. Django initialization order matters - use `django.setup()` before importing models
3. Redis is essential for production WebSocket applications
4. Proper Nginx configuration is crucial for routing different protocols

With this setup, you'll have a robust production environment that can handle both HTTP requests and WebSocket connections efficiently.

Remember to monitor your services, implement proper error handling, and test thoroughly in a staging environment before deploying to production.

Share this article