Troubleshooting
Ansible output
Prior to playbook invocation:
export ANSIBLE_STDOUT_CALLBACK=debug
# export ANSIBLE_STDOUT_CALLBACK=yaml
Django errors
Django logging is configured in settings to use the SystemD logs. You can inspect the logs by issuing the following command:
sudo journalctl -u gunicorn-production_website.service
If you want logging to a file, replace the following variable in the settings file website/config/settings/production.py`:
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"file": {
"level": "WARNING",
"class": "logging.FileHandler",
"filename": BASE_DIR.parent.parent.parent / "logs" / "django-warning.log",
},
},
"loggers": {
"django": {
"handlers": ["file"],
"level": "WARNING",
"propagate": True,
},
},
}
You can then access the log with:
sudo tail -n 50 /var/www/www.asastats.com/logs/django-warning.log
Invalid HTTP_HOST header
https://stackoverflow.com/a/49817720/11703358
if ( $host !~* ^(yourdomain.com|www.yourdomain.com)$ ) {
return 444;
}
Linux server errors
Check killed processes:
sudo dmesg -T | egrep -i 'killed process'+
The following setting will prevent OOM from killing the process:
[Service]
OOMScoreAdjust=-999
WebSocket and realtime
Engine-backed widgets (e.g. historic) open a WebSocket to their own consumer, which relays engine progress over a shared Channels-Redis bus. Common failure modes:
Consumer never reached
The page renders but the consumer’s connect never runs. If the browser’s Network → WS
tab shows nothing, the socket was never created client-side — confirm htmx loaded exactly
once (a second htmx core on the page silently drops ws-connect) and that the
ws-connect URL carries a real bundle. If instead the WS request shows a 404, the
route is not registered — check the widget’s routing.py is included and that
runserver is serving ASGI/Daphne (daphne listed above
django.contrib.staticfiles in INSTALLED_APPS).
Handshake succeeds but the widget stays busy
A 101 Switching Protocols means the consumer connected; if the widget then hangs, its
first engine call is failing or blocking — read the server log. A 403 from the engine
on the initial evaluate usually means the bundle has no stored address count yet
(processing writes it), while a connection error means the engine is not running or not
reachable.
Timeout reading from Redis
redis.exceptions.TimeoutError: Timeout reading from <host> in the consumer’s receive
loop is the Channels bus, not the app cache. Confirm the frontend and the engine target
the same Redis host, port and database, and that channels, channels_redis and
redis are pinned to a compatible set — a newer redis-py (RESP3) against an older
channels_redis produces exactly this timeout.
Debugging using headers
You can use a custom debugging middleware and inspect the headers through your browser’s DevTools/Network tab.
MIDDLEWARE = [
# ...
"core.middleware.DebugEnvMiddleware",
]
class DebugEnvMiddleware:
"""Stamp every response with which settings/session/cache the *serving*
process actually loaded. Compare a normal page (WSGI) with a /widgets/…
request (Daphne): any difference in SESSION or CACHE is the bug.
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
cache_default = settings.CACHES.get("default", {})
response["X-Debug-Env"] = (
f"MODULE={getattr(settings, 'SETTINGS_MODULE', 'None')} | "
f"SESSION={getattr(settings, 'SESSION_ENGINE', 'None')} | "
f"CACHE_BACKEND={cache_default.get('BACKEND', 'None')} | "
f"CACHE_LOC={cache_default.get('LOCATION', 'None')} | "
f"CACHE_PFX={cache_default.get('KEY_PREFIX', 'None')} | "
f"AUTH={request.user.is_authenticated}"
)
response["X-Debug-Cookie"] = request.META.get("HTTP_COOKIE", "MISSING")[:80]
from django.core.cache import cache
probe = f"probe:{request.META.get('HTTP_HOST','')}"
cache.set(probe, "seen", 30)
response["X-Debug-Cache-Probe"] = (
f"{probe}={cache.get(probe)} | id={id(cache._cache) if hasattr(cache,'_cache') else 'na'}"
)
# also surface the raw session lookup:
from importlib import import_module
engine = import_module(settings.SESSION_ENGINE)
sk = request.COOKIES.get("sessionid", "")
s = engine.SessionStore(sk)
response["X-Debug-Session"] = (
f"key={sk[:6]} exists={s.exists(sk) if sk else 'no-key'} keys={list(s.keys())}"
)
import hashlib
response["X-Debug-Secret"] = hashlib.sha256(settings.SECRET_KEY.encode()).hexdigest()[:12]
return response