Perch

Health endpoint

Expose one small JSON endpoint and Perch charts CPU, memory, disk, network, processes and Docker containers — and alerts when your app reports a failure or a resource crosses a limit.

Set up in 3 steps

  1. Step 1 of 3
    Create a health-endpoint monitor
    Go to Add monitor → Health endpoint. Enter where the endpoint will live and, optionally, CPU / memory / disk limits.
  2. Step 2 of 3
    Copy the secret
    The monitor page shows your secret. Perch sends it as Authorization: Bearer <secret>; your endpoint only answers when it matches.
  3. Step 3 of 3
    Add the endpoint and test it
    Add the endpoint with one of the Recipes below, set PERCH_HEALTH_TOKEN, and press Test endpoint — Perch shows exactly what it understood and what is missing.
No code changes possible (WordPress, a static site, a PHP host…)? Use Standalone: one Python file runs next to your site and reports the whole server. With Docker access it lists every container too.

Recipes

Each recipe uses one of three ready-made files — download them directly: perch_health.py · perch-health.js · perch-health.php.

  1. Download the agent (one Python file) on the server you want to watch:
    curl -fsSLO /integrations/perch_health.py
    pip3 install psutil   # optional, gives CPU / memory / processes on every OS
  2. Run it with your secret. It serves the health JSON on port 9105:
    PERCH_HEALTH_TOKEN=YOUR-SECRET nohup python3 perch_health.py > perch_health.log 2>&1 &
  3. Allow Perch to reach port 9105 in your firewall, then use this URL in Perch.

What Perch reads (Perch Health Spec v1)

Already have a health endpoint? Return this shape and Perch understands it. Every field is optional — send what you have; unknown fields are ignored. Respond with HTTP 200 (or 503 when failing).

GET /perch-health → application/json
{
  "status": "pass",
  "service": "orders-api",
  "version": "1.4.2",
  "checks": {
    "database": { "status": "pass", "latency_ms": 3 }
  },
  "redis": [
    { "name": "127.0.0.1:6379", "status": "pass", "latency_ms": 1.2, "version": "7.2.4", "role": "primary",
      "used_memory_bytes": 52428800, "maxmemory_bytes": 268435456, "connected_clients": 12, "evicted_keys": 0 }
  ],
  "system": {
    "hostname": "web-1",
    "os": "Linux",
    "uptime_seconds": 86400,
    "cpu":     { "percent": 37.5, "cores": 4, "load": [0.52, 0.41, 0.33] },
    "memory":  { "total_bytes": 8589934592, "used_bytes": 5261334938, "percent": 61.2 },
    "disks":   [{ "mount": "/", "total_bytes": 107374182400, "used_bytes": 77309411328, "percent": 72.0 }],
    "network": { "rx_bytes": 9123456789, "tx_bytes": 4567891234 }
  },
  "processes": {
    "total": 213,
    "top": [{ "pid": 1234, "name": "python", "cpu_percent": 12.1, "memory_percent": 3.2 }]
  },
  "containers": [
    { "name": "api", "image": "acme/api:1.4.2", "state": "running", "cpu_percent": 4.0, "memory_bytes": 268435456 }
  ]
}
FieldMeaning
statuspass, warn or fail (also accepts ok / healthy / degraded / error / down). Missing → derived from the HTTP code.
checksYour own checks (database, cache, queue…). A failing check is listed in the alert.
redis[]Redis your app uses: status, memory vs maxmemory, clients, evicted keys. The integration files fill it in by themselves from PERCH_REDIS_URL or REDIS_URL (else a Redis on 127.0.0.1:6379, if one answers); PERCH_REDIS_URL=off skips it.
system.cpu.percentCPU usage 0–100. load = 1, 5 and 15-minute load averages.
system.memoryGive percent, or used_bytes + total_bytes and Perch calculates it.
system.disks[]One entry per mount; Perch alerts on the fullest one.
system.networkCumulative byte counters (rx_bytes, tx_bytes). Perch turns them into in / out rates.
processesTotal count and up to 10 top processes.
containers[]Name, image, state, CPU and memory per container.

When does Perch alert?

SituationResult
Endpoint unreachable, times out, or returns something that is not JSONDown → incident + alert
status: "fail"Down; the alert names the failing checks
A Redis with status: "fail" (unreachable, wrong password)Down, e.g. Redis 127.0.0.1:6379 failing: connection refused
CPU / memory / fullest disk above the limit you setDown, e.g. Disk 93% > 90% on /
status: "warn"Shown on the dashboard, no alert
HTTP 401 / 403Down with a hint to check PERCH_HEALTH_TOKEN

Security

The recipes answer only when the Authorization header matches PERCH_HEALTH_TOKEN (constant-time comparison) and never include environment variables or command lines. Prefer HTTPS for endpoints on the public internet, or restrict the port to Perch in your firewall. You can create a new secret any time from the monitor page.

Troubleshooting

Test endpoint saysFix
Connection refused / timed outThe endpoint is not running or a firewall blocks Perch. For Standalone, open port 9105.
HTTP 401 — rejected Perch’s tokenSet PERCH_HEALTH_TOKEN to the secret on the monitor page and restart the app. On Apache + PHP, keep CGIPassAuth On.
Response is not JSONThe URL points at a web page (often a login redirect). Use the exact endpoint path.
`system.memory` missingOn macOS / Windows the Python file needs pip install psutil for CPU and memory.
Heartbeats →Server agent →