Skip to content

Usage

Setting up monitoring

You can point an uptime monitor such as Pingdom, StatusCake, or other uptime robots at your service. The /health/ endpoint returns an HTTP 200 when every check passes, and an HTTP 500 when any of them fails.

For step-by-step examples of multi-tier endpoint setups, including uptime monitoring, container probes, reverse-proxy configuration, and RSS/Atom integration into Slack or Matrix, see the Cookbook.

Getting machine-readable reports

Plain text

For simple monitoring and scripting, ask for plain text. Set the Accept HTTP header to text/plain, or pass format=text as a query parameter.

When everything passes, you get a plain text response with HTTP 200. When a check fails, you get HTTP 500:

$ curl -v -X GET -H "Accept: text/plain" http://www.example.com/health/

> GET /health/ HTTP/1.1
> Host: www.example.com
> Accept: text/plain
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8

CacheBackend: OK
DatabaseBackend: OK
S3BotoStorageHealthCheck: OK

$ curl -v -X GET http://www.example.com/health/?format=text

> GET /health/?format=text HTTP/1.1
> Host: www.example.com
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8

CacheBackend: OK
DatabaseBackend: OK
S3BotoStorageHealthCheck: OK

This format is handy for command-line tools and simple scripts that don't want to parse JSON.

JSON

Want machine-readable results? Set the Accept HTTP header to application/json, or pass format=json as a query parameter.

The endpoint returns a JSON response:

$ curl -v -X GET -H "Accept: application/json" http://www.example.com/health/

> GET /health/ HTTP/1.1
> Host: www.example.com
> Accept: application/json
>
< HTTP/1.1 200 OK
< Content-Type: application/json

{
    "CacheBackend": "working",
    "DatabaseBackend": "working",
    "S3BotoStorageHealthCheck": "working"
}

$ curl -v -X GET http://www.example.com/health/?format=json

> GET /health/?format=json HTTP/1.1
> Host: www.example.com
>
< HTTP/1.1 200 OK
< Content-Type: application/json

{
    "CacheBackend": "working",
    "DatabaseBackend": "working",
    "S3BotoStorageHealthCheck": "working"
}

OpenMetrics for Prometheus

If you monitor with Prometheus, request the OpenMetrics format:

$ curl http://www.example.com/health/?format=openmetrics

Prometheus can scrape these metrics directly.

RSS and Atom feeds

For feed readers and monitoring tools, request the RSS or Atom format:

$ curl http://www.example.com/health/?format=rss
$ curl http://www.example.com/health/?format=atom

You can also use the Accept header:

$ curl -H "Accept: application/rss+xml" http://www.example.com/health/
$ curl -H "Accept: application/atom+xml" http://www.example.com/health/

These endpoints always answer with HTTP 200. The feed lists each check, and failed checks show up as categories and item descriptions.

Writing a custom health check

You can write your own checks too. Inherit from HealthCheck and implement the run method.

health_check.HealthCheck dataclass

Bases: ABC

Base class for defining health checks.

Subclasses should implement the run method to perform the actual health check logic. The run method can be either synchronous or asynchronous.

Examples:

>>> import dataclasses
>>> from health_check.base import HealthCheck
>>>
>>> @dataclasses.dataclass
>>> class MyHealthCheck(HealthCheck):
...
...    async def run(self):
...        # Implement health check logic here

Subclasses should be dataclasses or implement their own __repr__ method to provide meaningful representations in health check reports.

Warning

The __repr__ method is used in health check reports. Consider setting repr=False for sensitive dataclass fields to avoid leaking sensitive information or credentials.

Source code in health_check/base.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@dataclasses.dataclass
class HealthCheck(abc.ABC):
    """
    Base class for defining health checks.

    Subclasses should implement the `run` method to perform the actual health check logic.
    The `run` method can be either synchronous or asynchronous.

    Examples:
        >>> import dataclasses
        >>> from health_check.base import HealthCheck
        >>>
        >>> @dataclasses.dataclass
        >>> class MyHealthCheck(HealthCheck):
        ...
        ...    async def run(self):
        ...        # Implement health check logic here

    Subclasses should be [dataclasses][dataclasses.dataclass] or implement their own `__repr__` method
    to provide meaningful representations in health check reports.

    Warning:
        The `__repr__` method is used in health check reports.
        Consider setting `repr=False` for sensitive dataclass fields
        to avoid leaking sensitive information or credentials.

    """

    @abc.abstractmethod
    async def run(self) -> None:
        """
        Run the health check logic and raise human-readable exceptions as needed.

        Exception must be reraised to indicate the health status and provide context.
        Any unexpected exceptions will be caught and logged for security purposes
        while returning a generic error message.

        Warning:
            Exception messages must not contain sensitive information.

        Raises:
            ServiceWarning: If the service is at a critical state but still operational.
            ServiceUnavailable: If the service is not operational.
            ServiceReturnedUnexpectedResult: If the check performs a computation that returns an unexpected result.

        """
        ...

    def pretty_status(self) -> str:
        """Return a human-readable status string, always 'OK' for the check itself."""
        return "OK"

    @property
    def labels(self) -> dict[str, str]:
        """Return a human-readable label for the check, defaulting to the class name."""
        return {
            "check": self.__class__.__name__,
        } | {
            field.name: str(value)
            for field in dataclasses.fields(self)
            if field.repr and (value := getattr(self, field.name)) is not None
        }

    async def get_result(self, executor: Executor | None = None) -> HealthCheckResult:
        loop = asyncio.get_running_loop()
        start = timeit.default_timer()
        try:
            await self.run() if inspect.iscoroutinefunction(
                self.run
            ) else await loop.run_in_executor(executor, self.run)
        except HealthCheckException as e:
            error = e
        except BaseException:
            logger.exception("Unexpected exception during health check")
            error = HealthCheckException("unknown error")
        else:
            error = None
        return HealthCheckResult(
            check=self,
            error=error,
            time_taken=timeit.default_timer() - start,
        )

labels property

Return a human-readable label for the check, defaulting to the class name.

pretty_status()

Return a human-readable status string, always 'OK' for the check itself.

Source code in health_check/base.py
73
74
75
def pretty_status(self) -> str:
    """Return a human-readable status string, always 'OK' for the check itself."""
    return "OK"

run() abstractmethod async

Run the health check logic and raise human-readable exceptions as needed.

Exception must be reraised to indicate the health status and provide context. Any unexpected exceptions will be caught and logged for security purposes while returning a generic error message.

Warning

Exception messages must not contain sensitive information.

Raises:

Type Description
ServiceWarning

If the service is at a critical state but still operational.

ServiceUnavailable

If the service is not operational.

ServiceReturnedUnexpectedResult

If the check performs a computation that returns an unexpected result.

Source code in health_check/base.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
@abc.abstractmethod
async def run(self) -> None:
    """
    Run the health check logic and raise human-readable exceptions as needed.

    Exception must be reraised to indicate the health status and provide context.
    Any unexpected exceptions will be caught and logged for security purposes
    while returning a generic error message.

    Warning:
        Exception messages must not contain sensitive information.

    Raises:
        ServiceWarning: If the service is at a critical state but still operational.
        ServiceUnavailable: If the service is not operational.
        ServiceReturnedUnexpectedResult: If the check performs a computation that returns an unexpected result.

    """
    ...

Django command

Run the Django command health_check from the shell, or schedule it with cron:

django-admin health_check health_check

The endpoint argument is the name of the health check URL pattern defined in your urls.py (see the installation guide). The command looks it up with reverse() and runs the checks over HTTP against the running server:

Database                 ... OK
CustomHealthCheck        ... Unavailable: Something went wrong!

Pass --no-http to skip the HTTP server entirely. Handy for container health checks that don't run a web server:

django-admin health_check health_check --no-http

A critical error exits the command with the code 1.

Performance tweaks

Every check runs asynchronously, via asyncio or a thread pool, depending on how each check is implemented. IO-bound checks run in parallel, so responses come back faster.

Synchronous checks (for example Database, Mail, or Storage) run in the event loop's default thread pool. That pool usually persists between requests. It keeps things fast, but it can grow memory usage. That's a problem for some applications, especially S3Storage, which uses thread-local connections.

To avoid that, use a custom executor that spins up a fresh thread pool per request and tears it down when the checks finish. Subclass HealthCheckView and override the get_executor method to return a context manager with a new ThreadPoolExecutor each time.

from concurrent.futures import ThreadPoolExecutor
from health_check.views import HealthCheckView


class CustomHealthCheckView(HealthCheckView):
    def get_executor(self):
        return ThreadPoolExecutor(max_workers=len(self.checks))

This gives every request its own thread pool. You keep the speed of concurrent execution for synchronous checks, but the memory stays under control.