cmd/prober) is the outside view of the platform: every 30 s it sends a real web
event and a real OTLP metric for the internal probe tenant, reads them back through the machine
API, checks the console, the Umami host, the tracker files against the release manifest, and
every node’s certificate by address. It must not run inside the system it watches, so it runs on
a host outside Azure. This document runs it on the owner’s Home LXC, beside the Uptime Kuma and
ntfy that already alert the owner’s phone, and makes Kuma alert on its results.
Nothing here changes the Home LXC by itself. The steps below are for the owner to run there.
1. What runs
infra/ansible/playbooks/probe-bundle.yml renders a self-contained Docker Compose bundle on the
controller, from the same templates the observability role uses:
Gatus is published only on the address in
PROBE_BIND; nothing listens publicly. Kuma reads
Gatus’s per-check status API, one Kuma monitor per check, and Kuma’s existing ntfy notification
delivers the alert.
2. Render the bundle (controller)
Needs the environment’s inventory (make <env>-apply), its SOPS file with the probe tenant
(platformctl probe-tenant create, bootstrap.md step 10) and the release
manifest:
probe bundle for production in .../resources/probe-bundle/production. The directory is mode
0700 under resources/, which Git ignores.
3. Run it on the Home LXC (owner)
Copy the directory and start it. The Home LXC’s containers reach host services at172.17.0.1
(the owner’s server notes, Docker networking), so Gatus is published there on port 8095. Check
first that nothing else uses the port: ssh home 'ss -ltn "( sport = :8095 )"' must list no
socket; otherwise change probe_bundle_port (-e probe_bundle_port=<port>).
Up, and a JSON list of endpoints within a minute. The bundle is its
own Compose project (veristar-probe-production) beside /home/docker/docker-compose.yml, so the
Home stack is not touched. The Home LXC resolves names through its own AdGuard; the platform’s
hostnames are under .app. On 2026-11-13 AdGuard answered a made-up .app name with no
address (not 0.0.0.0), so the TLD is not blocked as a whole. Check the real names once they
exist: dig +short in.veristar.app @192.168.1.102 must not answer 0.0.0.0.
Backups: the bundle holds only configuration and Gatus’s history; the Home restic job already
covers /home/docker unless an exclude matches.
4. Add the Uptime Kuma monitors (owner, Kuma web UI)
One monitor per check. For each, in Uptime Kuma: Add New Monitor.
The query is true only when the newest result is younger than three minutes and passed, so a
stopped prober alerts too, not only a failed check. Gatus page 1 with
pageSize=1 holds exactly
the newest result (checked with Gatus 5.38.0 on 2026-11-13).
The keys (Gatus key of group and name):
The full list is at
http://172.17.0.1:8095/api/v1/endpoints/statuses (field key). Kuma’s
own status page is behind Cloudflare Access, so these monitors are for the owner, not a public
status page.
5. Public status page and the alert heartbeat
The bundle has no public listener.status.<BASE_DOMAIN> (the status page) and the
Alertmanager watchdog push are served only when a probe host is in the inventory’s probe
group, with its own public address and Caddy (site.yml, play Probe). With the probe on the
Home LXC, the status page can be published by a Cloudflare Tunnel route to Gatus, for example
through the Home LXC’s existing cloudflared, as a separate decision of the owner: it is a
change to the Home LXC and to the Cloudflare account.
The nightly slo-report.yml workflow reads Gatus’s results from STATUS_BASE_URL
(https://status.<input I4>), so the monthly SLO report needs that publication too. Until then
the Gatus history on the Home LXC holds the measurements (about a week of raw results), and
Kuma keeps its own uptime history per monitor.
6. What was checked
ansible-playbook --syntax-checkandmake ansible-lintpass for the playbook and template (2026-11-13).- The Gatus API answers used by the Kuma query, with Gatus 5.38.0 and three pushed results:
pageSize=1returned only the newest (success: falseaftertrue,true,false). - Not run yet: the bundle against a live environment (there is none before the first apply).