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.

Updated · by PuzariCloud · commands checked against current releases

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 migrate runs.
  • Gunicorn worker timeouts. A view that takes longer than Gunicorn's timeout (30 s by default) gets its worker killed: [CRITICAL] WORKER TIMEOUT in the log and a 502 from the proxy.
  • Database connections. With CONN_MAX_AGE and many workers, connections can exceed the database's limit; without it, every request pays for a new connection.
  • Static files missing. collectstatic did not run or the storage is wrong: pages load without CSS and JavaScript but still return 200.
  • DEBUG left on. A deploy with DEBUG=True leaks settings and stack traces and keeps every SQL query in memory.

The signals that matter, with alert thresholds

SignalAlert whenWhy it matters
Health view (DB, cache)any check failedThe app cannot do its job.
Uptime of key pagesnon-2xx/3xx, or p95 > 1 sWhat users see.
500 errorsnew issue, or a spike after a deployBugs.
WORKER TIMEOUT in the Gunicorn loganySlow views killing workers.
Slow queries by total timea new entry at the topMissing index, N+1.
Management command heartbeatslateScheduled 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

Deployment checklist and unapplied migrations
python manage.py check --deploy
python manage.py migrate --check   # non-zero exit if unapplied
Health view by hand
curl -s https://example.com/healthz
Gunicorn worker timeouts
journalctl -u gunicorn --since today | grep 'WORKER TIMEOUT'
Is a static file actually served?
curl -sI https://example.com/static/admin/css/base.css | head -1

How to watch Django with Vigil

  1. 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).
  2. Add an Uptime monitor on the home page and a Keyword monitor for text that only renders when the page works.
  3. 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.
  4. For traces, use opentelemetry-instrumentation-django plus 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.
  5. Install the server agent, add the Gunicorn unit to the services to watch and a log_watch for WORKER TIMEOUT. For cron-run management commands, see monitoring cron jobs.
healthz view (urls.py: path("healthz", healthz))
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.

Vigil by PuzariCloud

Watch Django without stitching five tools together

Uptime and API checks, heartbeats, server and database metrics, traces, errors and logs in one place, with one incident and one alert when something breaks. Every feature is on every plan, including Free.