Django Channels and WebSockets Production Checklist

A production checklist for Django Channels: ASGI server choice, Redis channel layer, WebSocket proxy configuration, authentication and origin checks, consumer performance, scaling, timeouts and heartbeats, deploys, and monitoring.

Problem statement

Django Channels adds WebSockets and long-lived connections to Django, and with them a different set of production concerns: an ASGI server instead of (or beside) Gunicorn, a channel layer shared across processes, proxies that must upgrade connections and not time them out, authentication and origin checks for a protocol that bypasses CSRF, and clients that reconnect gracefully. This checklist covers what to verify before real users rely on live features.

Channels in productionASGI serverDaphne or UvicornChannel layerRedis, dedicatedProxyupgrade + timeoutsSecurity + clientsorigins, reconnectWebSockets need a shared layer, an upgrade-aware proxy, origin checks, and reconnecting clients.
  1. Serve ASGI with Daphne or Uvicorn (often under Gunicorn), supervised and behind Nginx or a load balancer.
  2. Use channels_redis on a dedicated Redis instance; never the in-memory layer.
  3. Configure the proxy for WebSocket upgrades and long idle timeouts; send heartbeats.
  4. Wrap the router in AllowedHostsOriginValidator and AuthMiddlewareStack; authorise per consumer.
  5. Keep consumers fast; move database writes and external calls to async code or Celery.
  6. Make clients reconnect with backoff and resync state after deploys.

Checklist

Server and routing

  • asgi.py uses ProtocolTypeRouter with HTTP to Django and WebSocket routes to consumers.
  • ASGI processes are supervised (systemd, containers) with graceful shutdown.
  • WebSocket paths (for example /ws/) route to ASGI processes; HTTP can stay on WSGI if preferred.

Channel layer

  • CHANNEL_LAYERS points at Redis (RedisChannelLayer or RedisPubSubChannelLayer).
  • Redis instance separate from cache and Celery broker, with memory limits and monitoring.
  • Capacity and expiry settings reviewed for message bursts.
what to verify and whyCheckFailure if missedChannel layerRedis, dedicatedmessages lost across processesProxyupgrade headers, long timeoutconnect fails or drops at 60 sSecurityorigin validator, authcross-site WebSocket hijackingConsumersfast, async, no blockinglatency for every connectionClientsreconnect with backoffdead UI after deploysEach area fails differently; all of them show up only under real usedjango-deployment.com
Test each area deliberately in staging with realistic connection counts.

Proxy and timeouts

  • Nginx location sets proxy_http_version 1.1, Upgrade, and Connection headers.
  • proxy_read_timeout (and load balancer idle timeout) well above the heartbeat interval.
  • Application-level heartbeats every 20-30 seconds for idle connections.
  • TLS terminates at the proxy; clients use wss://.

Security

  • AllowedHostsOriginValidator rejects WebSocket handshakes from other origins (WebSockets are not covered by CSRF).
  • AuthMiddlewareStack (or token auth middleware) populates scope["user"]; consumers check permissions for every group they join.
  • Message size limits and per-connection rate limits for user-sent messages.

Consumers and performance

  • Async consumers use database_sync_to_async or the async ORM; no blocking calls.
  • Heavy work (persistence, notifications, external APIs) is queued to Celery.
  • Group sizes and broadcast frequency are bounded; large fan-outs are throttled.

Deploys and monitoring

  • Rolling restarts; clients reconnect with exponential backoff and resync missed data.
  • Metrics: active connections per process, messages per second, Redis memory and CPU, consumer errors.
  • Load-tested with the expected number of concurrent connections.
before going live with WebSocketsSupervised ASGI server behind a proxygraceful shutdownDedicated Redis channel layernever in-memoryUpgrade headers, long timeouts, heartbeatsno 60 s dropsOrigin validator and per-group authno hijackingClient reconnect and load testdeploys and scaleLive features fail loudly in front of users; check these firstdjango-deployment.com
Revisit the list when adding new consumers or groups.

Explanation

WebSockets turn a request-response app into one with thousands of long-lived connections, each held by an ASGI process. Messages between users connected to different processes travel through the channel layer, so it must be shared and sized for fan-out. Proxies must explicitly support the protocol upgrade and tolerate idle periods. Because browsers send cookies with WebSocket handshakes from any origin and CSRF does not apply, origin validation and per-group authorisation are the security controls. Finally, connections inevitably drop, so clients must recover on their own.

Edge cases / notes

  • Mixed WSGI and ASGI: many teams keep HTTP on Gunicorn sync workers and route only /ws/ to ASGI.
  • Server-sent events are simpler than WebSockets for server-to-client updates and work with standard HTTP infrastructure.
  • Managed realtime services (Pusher, Ably) remove connection handling from your servers for very large audiences.
  • Mobile clients suspend sockets in the background; design for reconnection.

Next steps

FAQ

What do I need to run Django Channels in production?

An ASGI server, a Redis channel layer, a WebSocket-capable proxy, origin and auth checks, and reconnecting clients.

Can I use the in-memory channel layer in production?

No. It only works within a single process.

Are WebSockets protected by Django's CSRF?

No. Use AllowedHostsOriginValidator and authorise users in consumers.

Why do WebSockets disconnect after a minute?

Proxy or load balancer idle timeouts. Raise them and send heartbeats.

Keep going