Framework Guide
Django API Monitoring
Set up health check endpoints and uptime monitoring for your Django application. Works with Django REST Framework and standard Django views.
Why Monitor Your Django App?
Django applications rely on multiple moving parts in production -- database connections, cache backends, Celery workers, and static file storage. Any of these can fail independently while the Django process itself keeps running. External monitoring catches these partial failures before users start seeing 500 errors.
- Database connection drops -- PostgreSQL or MySQL connections can timeout or get reset, causing intermittent query failures
- Migration drift -- A deployment that skips migrations leaves your schema out of sync, causing silent data corruption
- Cache backend failures -- Redis or Memcached going down can cascade into slow queries and high database load
- Celery worker stalls -- Background task workers can silently stop processing jobs, building up a growing queue
Simple Health View
# health/views.py
from django.http import JsonResponse
from datetime import datetime
def health(request):
return JsonResponse({
'status': 'healthy',
'timestamp': datetime.utcnow().isoformat()
})
# urls.py
from django.urls import path
from health import views
urlpatterns = [
path('health/', views.health, name='health'),
]
Comprehensive Health Check
# health/views.py
from django.http import JsonResponse
from django.db import connection
from django.core.cache import cache
from django.conf import settings
from datetime import datetime
import time
def health(request):
"""Liveness probe - just confirms Django is running"""
return JsonResponse({'status': 'healthy'})
def ready(request):
"""Readiness probe - checks all dependencies"""
checks = {}
healthy = True
# Check database
try:
start = time.time()
with connection.cursor() as cursor:
cursor.execute('SELECT 1')
cursor.fetchone()
checks['database'] = {
'status': 'ok',
'response_time_ms': round((time.time() - start) * 1000, 2)
}
except Exception as e:
healthy = False
checks['database'] = {'status': 'error', 'message': str(e)}
# Check cache (Redis/Memcached)
try:
start = time.time()
cache.set('health_check', 'ok', 10)
cache.get('health_check')
checks['cache'] = {
'status': 'ok',
'response_time_ms': round((time.time() - start) * 1000, 2)
}
except Exception as e:
healthy = False
checks['cache'] = {'status': 'error', 'message': str(e)}
response = {
'status': 'healthy' if healthy else 'unhealthy',
'timestamp': datetime.utcnow().isoformat(),
'checks': checks,
'version': getattr(settings, 'VERSION', 'unknown')
}
status_code = 200 if healthy else 503
return JsonResponse(response, status=status_code)
Using django-health-check
For a more robust solution, use the django-health-check package:
# Install
pip install django-health-check
# settings.py
INSTALLED_APPS = [
# ...
'health_check',
'health_check.db',
'health_check.cache',
'health_check.storage',
'health_check.contrib.migrations',
'health_check.contrib.celery', # if using Celery
'health_check.contrib.redis', # if using Redis
]
# urls.py
urlpatterns = [
path('health/', include('health_check.urls')),
]
Django REST Framework
# health/views.py
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status
from django.db import connection
@api_view(['GET'])
@permission_classes([AllowAny])
def health(request):
return Response({'status': 'healthy'})
@api_view(['GET'])
@permission_classes([AllowAny])
def ready(request):
try:
with connection.cursor() as cursor:
cursor.execute('SELECT 1')
return Response({
'status': 'healthy',
'checks': {'database': 'ok'}
})
except Exception as e:
return Response({
'status': 'unhealthy',
'checks': {'database': str(e)}
}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
Exclude from Middleware
Skip authentication and logging for health checks:
# middleware.py
class HealthCheckMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.path in ['/health/', '/ready/']:
# Skip other middleware for health checks
from health.views import health
return health(request)
return self.get_response(request)
Best Practices
- Separate liveness and readiness — /health for "is Django running", /ready for "are dependencies working"
- Skip authentication — Monitoring services need unauthenticated access
- Check migrations — Ensure database schema is up to date
- Add timeouts — Don't let slow checks hang forever