What counts as healthy
A ping goes out only when all of these hold:- the database answers,
- the reconcile loop completed a pass within
APP_HEARTBEAT_RECONCILE_WINDOWS(default 3) resync intervals, - the local agent is registered (skipped on a fresh install with no local node yet).
Setup
Dashboard: Settings, Notification channels, “Off-host heartbeat”. Paste the URL and save. The URL is stored encrypted and only its host is shown afterwards. CLI:
An invalid URL stops the server at boot with a clear message instead of silently never pinging.
Pair it with a monitor
Healthchecks.io: create a check, set its period to your interval and its grace to a few minutes, and use its ping URL (https://hc-ping.com/<uuid>). For the failure URL use the same URL with /fail appended.
Uptime Kuma: add a Push monitor, set the heartbeat interval a little above APP_HEARTBEAT_INTERVAL, and use the push URL it shows. Older versions only accept GET on push URLs, so set APP_HEARTBEAT_METHOD=GET. For the failure URL use the same push URL with status=down in the query.
Better Stack and similar heartbeat monitors work the same way: copy the heartbeat URL, match the period.
Run Uptime Kuma on a different machine or provider than the server it watches. A monitor on the same box dies with it.
What is sent
By default a small JSON body:{"status":"ok","version":"...","uptime_seconds":123}. A failure ping adds a one word reason (store, reconcile or agent). No hostnames, no app names, no identifiers. Set APP_HEARTBEAT_BODY=off to send nothing. Redirects to another host are refused, the URL never appears in logs or API responses (only its host), and private destinations are blocked unless you allow them.
When only the control plane dies
The heartbeat above is sent by the control plane, so it cannot tell you about a control plane that is dead while the box is still up (for example a crashed process that nothing restarts). Agents cover that: when a node has had no contact with the control plane forAPP_AGENT_CP_SILENCE_GRACE, it pings a URL you choose, naming the node in the body, and stops as soon as the session is back.
Set it in the dashboard (the “Agent silence URL” field on the same card) or with the CLI:
/fail appended). The control plane pushes the URL to connected agents over the existing mTLS session, within APP_AGENT_CP_SILENCE_PUSH_EVERY of a change. The agent keeps it in memory only, so an agent restarted while the control plane is already down has no URL until it connects once. The URL is stored encrypted and never logged, only its host.
Agent environment:
Control plane:
APP_AGENT_CP_SILENCE_PUSH_EVERY (default 30s) is how often live sessions re-check the stored URL.