Cookbook
This cookbook shows how to set up multiple health check endpoints. Each endpoint has a different audience and use case.
A good setup has three tiers of endpoints:
| Tier | Purpose | Consumers |
|---|---|---|
| Node | Hardware & OS resources | Kubernetes, Docker, reverse proxies |
| Application | App-level services | Uptime monitors, on-call alerts |
| Pipeline | Upstream dependencies | CI/CD, developer Slack/Matrix channels |
Node health checks
Node checks make sure that the server has enough resources to run the application. Use them for liveness and readiness probes in container orchestrators, for example Kubernetes, Docker, and Podman. You can also use them for reverse-proxy checks in HAProxy, nginx, Caddy, and Traefik.
The psutil extra provides OS resource checks for CPU, memory, and disk usage.
pip install "django-health-check[psutil]"
Add the node endpoint to your URL configuration.
# urls.py
from django.urls import include, path
from health_check.views import HealthCheckView
node_checks = [
"health_check.contrib.psutil.CPU",
"health_check.contrib.psutil.Memory",
"health_check.contrib.psutil.Disk",
]
urlpatterns = [
# …
path(
"health/",
include(
[
path(
"node/",
HealthCheckView.as_view(checks=node_checks),
name="health_check-node",
),
]
),
)
]
Tip
See the Security section of the installation guide.
Kubernetes probes
Kubernetes uses liveness and readiness probes to decide when to restart a pod. The probes use HTTP. Thus they also prove that the full HTTP stack operates correctly.
Note
If the WSGI/ASGI server binds to 0.0.0.0, the kubelet can reach the
httpGet probes. A bind to 127.0.0.1 is not sufficient.
The configuration for this health check endpoint is:
# deployment.yaml
livenessProbe:
httpGet:
path: /health/node/
port: 8000
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /health/node/
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
See the Kubernetes documentation for more information.
Docker / Podman
Compose has no native HTTP probes. Thus this setup uses the
health_check command. The command does not need
CURL in the image. It can emulate proxy requests to satisfy HTTPS
requirements.
# compose.yml
services:
web:
# … your service definition …
healthcheck:
test: ["CMD", "python", "manage.py", "health_check", "health_check-node", "web:8000"]
interval: 30s
timeout: 10s
Load balancers
Most reverse proxies can load balance. After configuration, they send traffic only to healthy instances.
The configuration for Caddy is:
# Caddyfile
example.com {
reverse_proxy localhost:8000 {
health_uri /health/node/
health_interval 30s
health_timeout 5s
}
}
The configuration for Traefik is:
# compose.yml
services:
app:
image: myapp
labels:
- "traefik.http.services.myapp.loadbalancer.healthcheck.path=/health/node/"
- "traefik.http.services.myapp.loadbalancer.healthcheck.interval=30s"
- "traefik.http.services.myapp.loadbalancer.healthcheck.timeout=5s"
The configuration for Nginx is:
# nginx.conf
upstream myapp {
server localhost:8000;
health_check interval=30s timeout=5s uri=/health/node/;
}
The configuration for HAProxy is:
# haproxy.cfg
backend myapp
server app1 localhost:8000 check inter 30s fall 3 rise 2
http-check expect status 200
http-check send meth GET uri /health/node/
Application health checks
Application checks make sure that each production service of your application is reachable and operates correctly. Uptime monitors, for example Pingdom, Better Uptime, or StatusCake, watch these endpoints. They notify on-call engineers when a service fails.
The full stack includes databases, caches, message brokers, email, and storage. To cover the full stack, install these extra dependencies:
pip install "django-health-check[redis,rabbitmq,celery,crontask]"
# urls.py
from datetime import timedelta
from django.urls import include, path
from health_check.views import HealthCheckView
from redis.asyncio import Redis as RedisClient
application_checks = [
# Django built-ins
"health_check.Cache",
"health_check.Database",
"health_check.Mail",
"health_check.Storage",
# Message brokers & caches
(
"health_check.contrib.redis.Redis",
{"client_factory": lambda: RedisClient.from_url("redis://localhost:6379")},
),
(
"health_check.contrib.rabbitmq.RabbitMQ",
{"amqp_url": "amqp://guest:guest@localhost:5672//"},
),
"health_check.contrib.celery.Ping",
"health_check.contrib.crontask.Scheduler",
]
urlpatterns = [
# …
path(
"health/",
include(
[
# other endpoints …
path(
"application/",
HealthCheckView.as_view(
checks=application_checks,
max_age=timedelta(seconds=30),
allow_anonymous=True,
),
name="health_check-application",
),
]
),
)
]
Point the uptime monitor at https://example.com/health/application/.
The endpoint returns HTTP 200 when all checks pass. It returns HTTP 500 when a
check fails. Uptime monitors expect these codes.
Anonymous requests can share a cached response for 30 seconds. Send
Authorization: Bearer $HEALTH_CHECK_AUTH_TOKEN to bypass the cache. The view
runs every check. The monitor always sees a fresh result. See
Security to create the token.
Pipeline health checks
Pipeline checks add upstream provider status to your application checks. This status includes cloud platforms, PaaS providers, and third-party services. The development team watches these checks with RSS/Atom feeds in Slack or Matrix. Then upstream outages become known before they become support tickets.
Install the extras that read the feeds from cloud providers:
pip install "django-health-check[rss,atlassian]"
Add the pipeline endpoint to your URL configuration.
# urls.py
from datetime import timedelta
from django.urls import include, path
from health_check.views import HealthCheckView
application_checks = [
# configured previously …
]
pipeline_checks = [
# You may want to include application health alerts here too
*application_checks,
# Cloud provider status (pick the ones relevant to your stack)
# GitHub status; to filter by a specific component, use a
# tuple like ("health_check.contrib.atlassian.GitHub", {"component": "<exact name from githubstatus.com>"})
"health_check.contrib.atlassian.GitHub",
"health_check.contrib.atlassian.Cloudflare",
(
"health_check.contrib.rss.AWS",
{"region": "eu-west-1", "service": "s3"},
),
]
urlpatterns = [
# …
path(
"health/",
include(
[
# other endpoints …
path(
"pipeline/",
HealthCheckView.as_view(
checks=pipeline_checks,
max_age=timedelta(seconds=30),
allow_anonymous=True,
),
name="health_check-pipeline",
),
]
),
)
]
The pipeline endpoint is public because Slack and Matrix cannot send an
Authorization header. Set allow_anonymous=True here.
The short max_age applies to the cached feed while authenticated probes stay
fresh.
Slack or Matrix
You can now tell engineers about upstream outages in Slack or Matrix.
Subscribe to the RSS feed in Slack:
- Install the Slack RSS App.
- In your
#opsor#incidentschannel, run:/feed subscribe https://www.example.com/health/pipeline/?format=rss
Or subscribe in Matrix:
# config.toml
[[bridge]]
name = "Pipeline Health Monitor"
feed_url = "https://example.com/health/pipeline/?format=rss"
room_id = "!YourRoomId:matrix.org"
Complete example
# urls.py
from datetime import timedelta
from django.urls import include, path
from health_check.views import HealthCheckView
from redis.asyncio import Redis as RedisClient
node_checks = [
"health_check.contrib.psutil.CPU",
"health_check.contrib.psutil.Memory",
"health_check.contrib.psutil.Disk",
]
application_checks = [
"health_check.Cache",
"health_check.Database",
"health_check.Mail",
"health_check.Storage",
(
"health_check.contrib.redis.Redis",
{"client_factory": lambda: RedisClient.from_url("redis://localhost:6379")},
),
(
"health_check.contrib.rabbitmq.RabbitMQ",
{"amqp_url": "amqp://guest:guest@localhost:5672//"},
),
"health_check.contrib.celery.Ping",
"health_check.contrib.crontask.Scheduler",
]
pipeline_checks = [
*application_checks,
(
"health_check.contrib.atlassian.GitHub",
{"component": "Actions"},
),
"health_check.contrib.atlassian.Cloudflare",
(
"health_check.contrib.rss.AWS",
{"region": "eu-west-1", "service": "s3"},
),
]
urlpatterns = [
path(
"health/",
include(
[
# Tier 1 – node: liveness & readiness probes
path(
"node/",
HealthCheckView.as_view(checks=node_checks),
name="health_check-node",
),
# Tier 2 – application: uptime monitors & on-call alerts
path(
"application/",
HealthCheckView.as_view(
checks=application_checks,
max_age=timedelta(seconds=30),
allow_anonymous=True,
),
name="health_check-application",
),
# Tier 3 – pipeline: developer RSS/Atom feeds (Slack, Matrix)
path(
"pipeline/",
HealthCheckView.as_view(
checks=pipeline_checks,
max_age=timedelta(seconds=30),
allow_anonymous=True,
),
name="health_check-pipeline",
),
]
),
),
]