How to monitor Django
Monitor a Django app with a health view that checks the database and cache, watched by an app-health monitor; an uptime check on key pages; error tracking for 500s through the Sentry SDK; and traces for slow views and queries via OpenTelemetry. Add the Gunicorn service, worker timeouts in its log, and heartbeats for management commands run by cron.
What can go wrong with Django
- Migrations not applied. A deploy ships code that expects a new column; every view touching that model returns 500 until
migrateruns. - Gunicorn worker timeouts. A view that takes longer than Gunicorn's
timeout(30 s by default) gets its worker killed:[CRITICAL] WORKER TIMEOUTin the log and a 502 from the proxy. - Database connections. With
CONN_MAX_AGEand many workers, connections can exceed the database's limit; without it, every request pays for a new connection. - Static files missing.
collectstaticdid not run or the storage is wrong: pages load without CSS and JavaScript but still return 200. - DEBUG left on. A deploy with
DEBUG=Trueleaks settings and stack traces and keeps every SQL query in memory.
The signals that matter, with alert thresholds
| Signal | Alert when | Why it matters |
|---|---|---|
| Health view (DB, cache) | any check failed | The app cannot do its job. |
| Uptime of key pages | non-2xx/3xx, or p95 > 1 s | What users see. |
| 500 errors | new issue, or a spike after a deploy | Bugs. |
| WORKER TIMEOUT in the Gunicorn log | any | Slow views killing workers. |
| Slow queries by total time | a new entry at the top | Missing index, N+1. |
| Management command heartbeats | late | Scheduled work not happening. |
Thresholds are starting points. Tune them after a week of data: an alert nobody acts on should be raised or removed.
How to check Django by hand
python manage.py check --deploy python manage.py migrate --check # non-zero exit if unapplied
curl -s https://example.com/healthz
journalctl -u gunicorn --since today | grep 'WORKER TIMEOUT'
curl -sI https://example.com/static/admin/css/base.css | head -1
How to watch Django with Vigil
- Add the health view below and create an App health monitor on it in New monitor. Vigil reads each named check and alerts per check ("database is failed"), not just "the page is down". Add a secret and check it in the view if the endpoint is public (Vigil sends it as
vigil-health-secret). - Add an Uptime monitor on the home page and a Keyword monitor for text that only renders when the page works.
- Create a project under Errors and pass its DSN to
sentry_sdk.init(); the Django integration is enabled automatically, with the request, user and release on every issue. - For traces, use
opentelemetry-instrumentation-djangoplus the instrumentation for your database driver and send to the OTLP endpoint (quick start). You get views with latency histograms, slow queries and N+1 detection. - Install the server agent, add the Gunicorn unit to the services to watch and a
log_watchforWORKER TIMEOUT. For cron-run management commands, see monitoring cron jobs.
from django.core.cache import cache
from django.db import connection
from django.http import JsonResponse
def healthz(request):
checks = {}
try:
connection.ensure_connection()
checks["database"] = {"status": "ok"}
except Exception as e:
checks["database"] = {"status": "failed", "message": str(e)[:200]}
try:
cache.set("healthz", 1, 5)
checks["cache"] = {"status": "ok" if cache.get("healthz") == 1 else "failed"}
except Exception as e:
checks["cache"] = {"status": "failed", "message": str(e)[:200]}
ok = all(c["status"] == "ok" for c in checks.values())
# 200 either way: Vigil reads the per-check status from the body
return JsonResponse({"status": "ok" if ok else "failed", "checks": checks})
Every alert goes to the channels you pick: email, Slack, Telegram, Discord, Microsoft Teams, Pushover, webhooks or on-call paging. Failures are confirmed by a second check before anyone is woken up.
Django monitoring FAQ
Can I use django-health-check?
It is a fine library, but its JSON reports "working" per plugin, which is not one of the shapes Vigil reads. A 20-line view like the one above gives per-check alerts; or monitor django-health-check's page with an API check asserting on its text.
Why does the health view return 200 when a check fails?
So the monitor can read which check failed. If you also use the endpoint for a load balancer, add a second URL that returns 503 on failure.
Does Vigil's own Python SDK trace Django?
The Vigil SDK wraps any WSGI app and instruments SQLAlchemy, requests, httpx and redis, but not the Django ORM. For ORM queries, OpenTelemetry's Django and database instrumentations are the better fit.