Skip to content

mirawara/healdy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Healdy

Healthcheck aggregator for Docker containers. Healdy exposes a single FastAPI endpoint that groups multiple containers into a named service and reports an aggregated health status.

Overview

  • Reads a JSON config that maps a service name to a list of container selectors (names, glob patterns, or labels).
  • Queries the local Docker Engine for each container's state and healthcheck status.
  • Returns 200 OK when all containers are healthy, or 503 when any are missing, unhealthy, or Docker API is unavailable.

Features

  • Aggregated health per logical service
  • Supports exact names, glob patterns, and label selectors
  • Verbose or minimal responses via VERBOSE
  • Config file hot-reload when the file changes

Quick Start (Docker Compose)

docker compose up --build

Then call:

curl http://localhost:18080/healthcheck/keycloak

The provided docker-compose.yml mounts the Docker socket (read-only) and the config file. It exposes the API on port 18080.

Configuration

By default, Healdy reads the config from /config/services.json. This repo ships an example file at config/config.json, which is mounted by Docker Compose.

Example config:

{
  "keycloak": {
    "containers": ["keycloak", "keycloak-db"]
  },
  "observability": {
    "containers": ["grafana", "prometheus", "loki"]
  },
  "webapp": {
    "containers": [
      "webapp",
      "webapp-*",
      {"label": "com.docker.compose.service=webapp-worker"}
    ]
  }
}

Selectors in containers can be:

  • Exact container names (strings)
  • Glob patterns (strings using *, ?, [])
  • Label selectors ({"label": "key=value"} or {"label": "key"})

API

GET /healthcheck/{service_name}

Returns the aggregated health status for the service defined in the config.

Possible responses:

  • 200 OK when all containers are healthy
  • 503 Service Unavailable when any container is unhealthy, starting, missing a healthcheck, missing entirely, or the Docker API is unavailable
  • 404 Not Found when the service name is not in the config

Response shape (ok, verbose default):

{
  "status": "ok",
  "service": "keycloak",
  "checked": [
    {
      "name": "keycloak",
      "id": "a1b2c3d4e5f6",
      "status": "running",
      "health": "healthy",
      "exit_code": 0
    }
  ]
}

Response shape (degraded, verbose default):

{
  "status": "degraded",
  "service": "webapp",
  "unhealthy": [
    {
      "name": "webapp-worker",
      "id": "f1e2d3c4b5a6",
      "status": "running",
      "health": "unhealthy",
      "exit_code": 1,
      "reason": "unhealthy"
    },
    {
      "reason": "missing-container",
      "containers": ["webapp-db"]
    }
  ],
  "checked": [
    {
      "name": "webapp",
      "id": "abcd1234efgh",
      "status": "running",
      "health": "healthy",
      "exit_code": 0
    }
  ],
  "expected": ["webapp", "webapp-*", {"label": "com.docker.compose.service=webapp-worker"}]
}

expected echoes the selectors from the config (strings or label objects).

Response shape (minimal, VERBOSE=false):

HTTP status codes remain the same; only the body is reduced to status.

{ "status": "ok" }
{ "status": "degraded" }
{ "status": "not-found" }

Reasons reported in unhealthy:

  • no-healthcheck when a container has no Docker healthcheck
  • unhealthy when the healthcheck reports unhealthy
  • starting (or other Docker health states) when the healthcheck is not healthy
  • any Docker container status other than running (e.g. exited, paused, restarting)
  • missing-container when a container selector in the config cannot be found (missing entries may include pattern: or label: prefixes)

Environment Variables

  • CONFIG_PATH (default: /config/services.json)
  • DOCKER_TIMEOUT in seconds (default: 3)
  • VERBOSE (default: true) accepts true/false, 1/0, yes/no, on/off

Run Locally (Without Docker)

python -m venv .venv
source .venv/bin/activate
pip install -r src/requirements.txt
uvicorn app:app --host 0.0.0.0 --port 8080 --app-dir src

Then call:

curl http://localhost:8080/healthcheck/keycloak

Reverse Proxy (Apache2 / Nginx)

If you place a proxy in front of Healdy, you can map a public /healthcheck endpoint to a specific service. Replace <service_name> with a service key from your config.

Apache2 (mod_proxy):

ProxyPass "/healthcheck" "http://healdy:8080/healthcheck/<service_name>"
ProxyPassReverse "/healthcheck" "http://healdy:8080/healthcheck/<service_name>"

Nginx:

location /healthcheck {
    proxy_pass http://healdy:8080/healthcheck/<service_name>;
}

With this setup you can check the health at https://example.it/healthcheck and keep the service behind the proxy fully transparent. For example, integrating it with Gatus (https://github.com/TwinProduction/gatus) is handy. The public endpoint path is fully customizable in your proxy configuration.

Notes & Security

  • This service needs access to the Docker API via /var/run/docker.sock. That grants high privileges on the host; run it in a trusted environment.
  • Container names in the config must match docker ps names, not image names.
  • The config file is cached and reloaded automatically when its file timestamp changes.

Contributing

Issues and pull requests are welcome. Please include a clear description and reproduction steps when reporting bugs.

License

MIT. See LICENSE for details.

About

Healthcheck aggregator for Docker containers. Healdy exposes a single FastAPI endpoint that groups multiple containers into a named service and reports an aggregated health status.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors