This episode helps you know whether the system is alive or dead before users report it: build a /health endpoint, collect metrics with django-prometheus, and set uptime alerts. The result is minimal but sufficient monitoring: alive/dead status, request counts, and 5xx errors. Following episode 7 on centralized logging and error capture with Sentry, this episode moves from “recording incidents” to “detecting incidents the moment they happen”.
1. The /health endpoint: what does a load balancer need?
A load balancer does not read logs — it calls one URL and checks the status code. A good /health endpoint must be cheap, fast, and check exactly what decides serving ability: the database connection. Do not check heavy things like sending a test email or calling an external API, because they slow the health check and report false failures. Return 200 when the database answers, 503 when it does not.
# monitoring/views.pyfrom django.db import connectionfrom django.http import JsonResponsedef liveness(request): return JsonResponse({"status": "ok"})def readiness(request): try: with connection.cursor() as cursor: cursor.execute("SELECT 1") except Exception: return JsonResponse({"status": "not_ready"}, status=503) return JsonResponse({"status": "ready"})
# monitoring/urls.pyfrom django.urls import pathfrom . import viewsurlpatterns = [ path("health/live/", views.liveness, name="liveness"), path("health/ready/", views.readiness, name="readiness"),]
2. Liveness vs. readiness
Merging these two concepts into one endpoint causes trouble at deploy time. Liveness answers “is the process still alive?”, readiness answers “is it ready to take traffic yet?”. While the app is running migrations, readiness should return 503 so the load balancer pauses sending requests, while liveness stays 200 so the orchestrator does not kill the process needlessly. For the expense-tracking app, readiness checks the database and Redis.
3. Metrics with django-prometheus
Logs tell you what happened to one specific request; metrics show the trend across the whole system. Install django-prometheus, add the middleware, and expose /metrics for Prometheus to scrape. You get request counts per view, processing times, and error counts. The Prometheus format works out of the box with Grafana and Alertmanager. Protect /metrics with an internal IP allowlist or basic auth.
# settings.py (production)INSTALLED_APPS = [ "django_prometheus", # ...]MIDDLEWARE = [ "django_prometheus.middleware.PrometheusBeforeMiddleware", # ... other middleware ... "django_prometheus.middleware.PrometheusAfterMiddleware",]
# urls.py (project)from django.urls import include, pathurlpatterns = [ path("", include("django_prometheus.urls")), path("", include("monitoring.urls")), # ...]
4. Setting uptime alerts from metrics
Metrics without alerts are just pretty charts. Start with three alerts: health check failing twice in a row, 5xx errors above 1% over 5 minutes, and p95 latency above 2 seconds over 10 minutes. Each alert must map to a concrete action: check logs per episode 7, inspect the latest deploy, or open the runbook. If an alert fires and nobody knows what to do, fix the runbook first.
5. Verifying after setup
Run the server, call /health and /metrics with curl, stop the database to see /health flip to 503, then restart it and confirm readiness recovers. Good monitoring means fast detection and clear action.
curl -i http://localhost:8000/health/live/curl -i http://localhost:8000/health/ready/curl http://localhost:8000/metrics | head -20
Applying it to your project
For the expense-tracking app, start now: add /health and /health/ready, wire django-prometheus into staging first, and set a single uptime alert to the team’s chat channel. When the first alert fires at the right moment, expand to business metrics like new transactions per hour.
Related posts
- Django Running in Production #7: Centralized logging and error capture with Sentry
- Django Running in Production #6: Safe migrations on production — backwards-compatible patterns, zero-downtime deploys
- Django Running in Production #5: Static and media — WhiteNoise, S3/MinIO and cache headers
FAQ
What should /health check?
Only what decides the ability to serve requests, usually the database and cache. Avoid non-essential external services because they cause false alarms.
Should /metrics be public?
No. The metrics endpoint exposes internal numbers and can be abused to probe the system. Restrict it by internal IP or basic auth.
How is django-prometheus different from logging processing time myself?
Logs record individual requests in isolation, making trends hard to aggregate. Metrics are pre-aggregated over time, making charts and alerts easy.
