Health checks matter more in the age of AI agents
An energy integration in my Home Assistant had been broken for months before I noticed. Now my servers, backups and house report success, so I hear when something goes quiet.
Updated
Tags: ai · agents · reliability · monitoring · home-automation
On this page
I recently found out that Home Assistant's Octopus Energy integration had been broken for months. Octopus had changed its API, and the version of the integration I had installed no longer worked with it. Its sensors had gone unavailable, and it only came to light when I went looking for the data to check my energy bills. Fixing it took a newer version of the integration and a new API key.
Nothing told me, because nothing expected that data to keep arriving. I now set things up the other way round: each job and server reports when it has worked, and a service outside the house tells me when those reports stop.
The software around my code is changing faster
I usually have several coding agents running at once, so I ship a lot of changes. Each one goes through automated tests, several rounds of adversarial review by agents told to find what's wrong, and agents that test the product the way a user would.
None of that covers the software I depend on. Closed-source products and SaaS APIs change without showing me the code, and I hear about a change when the vendor announces it, if I'm paying attention. Open-source projects publish their changes, but someone still has to read them. Either way, new bugs and security vulnerabilities keep arriving in software I didn't write.
Everyone else is shipping with AI too. In Google's 2025 DORA report, 90% of the nearly 5,000 technology professionals surveyed said they use AI at work. AI adoption now has a positive relationship with software delivery throughput, but it "does continue to have a negative relationship with software delivery stability". The report's explanation is that without strong testing and fast feedback loops, "an increase in change volume leads to instability". A survey can't establish cause, but the volume part matches my experience: the software I run changes faster than I can review it. I can't fix that, but I can find out quickly when something I rely on stops working.
Report success and alert on silence
Health checks are a habit I picked up in 2019 at Capital One, where I went through a phase of building them into everything I built. It meant we heard quickly when something we owned broke. I've largely kept the habit up since, and it matters more now than it did then.
The usual way to alert is to catch an error and send a message, which only works if the code is still running and gets far enough to report. A power cut doesn't raise an exception, and neither does a job that stopped being scheduled after an upgrade. An integration whose errors only reach a log nobody reads is just as quiet.
Healthchecks.io turns this around. It's a dead man's switch for scheduled work: it never probes your service, and waits for your service to check in instead. Each check has a URL that whatever you're monitoring requests when it succeeds. You set a period, how often to expect a ping, and a grace time, how much longer to wait before a late ping counts as missing. If the ping still hasn't arrived when the grace time ends, the check goes down and alerts you. The free plan covers 20 checks.
The trade-off is speed. An error alert can fire as soon as something fails, while a heartbeat has to wait for its deadline. I want both.
A job runs every 10 minutes. The problem starts just after the run at minute 0. The heartbeat check expects a ping every 10 minutes, with 5 minutes' grace.
Alert on failure
Nothing sent
The machine that would send the alert is the one that's off.
Alert on missing success
Down, alert sent at minute 15
Healthchecks.io noticed the silence from outside.
Keep the watcher outside the thing it watches. If your monitoring runs on the same servers, region or cloud account as your product, it can go down with it, and the power cut in the demo would never reach anyone.
The ping should only go out after the outcome you care about. If Home Assistant had pinged a check just to say it was running, it would have kept pinging through months of missing Octopus data. For a backup, it's two pings around the work. The start ping means a job that hangs also alerts, and sending the exit status reports a failure straight away:
# Illustrative backup job
URL=https://hc-ping.com/your-check-uuid
curl -fsS -m 10 --retry 5 -o /dev/null "$URL/start"
./run-backup && ./verify-backup
curl -fsS -m 10 --retry 5 -o /dev/null "$URL/$?"Wiring it into my house
I use Healthchecks.io with alerts to the ntfy app on my phone, and email as a backup. ntfy is free, doesn't need an account, and Healthchecks.io supports it directly.
In the house
Leak and smoke sensors, internet failover
- reports a change
In the house
Home Assistant
- sends an alert
My phone
ntfy
In the house
Servers and backup jobs
- check in
Hosted
Healthchecks.io
Expects each ping on schedule, plus grace time
- alerts on failure or silence
My phone and inbox
ntfy and email
Every backup job reports its start and exit status, as in the snippet above. Each of my home servers, a Mac mini and two Raspberry Pis, runs a health script every few minutes that checks its containers, disks and services. It pings when everything passes, and sends a failure signal if a problem is still there on the next run, so a container restarting once doesn't wake anyone.
One script also checks that every security camera is still recording, because the camera software shows a dropped camera in its own interface without alerting anyone. If all three servers stop checking in together, whatever took them offline, Healthchecks.io notices from outside.
In the house, Home Assistant is set up to send a top-priority ntfy alert, naming the sensor's location, as soon as a leak sensor gets wet, and smoke, heat and carbon monoxide alarms go the same way. I get an alert after a minute on the 4G backup, and another when fibre comes back, with how long it was out. If the internet seems slow, I know whether the house is on 4G before I start troubleshooting my own machine.
If you only alert on leaks, an offline leak sensor looks exactly like a dry one until the floor gets wet, because a sensor with a flat battery never reports water. So Home Assistant also watches the sensors themselves. If a leak or smoke sensor stops reporting, I hear about it within a few hours, and it's listed again every morning until it's fixed.
Choosing what to watch in an app
In a product, the same idea matters most for the few things that have to keep working.
- Ask what would happen if each part stopped for an hour and nobody noticed. The answers that hurt are usually signing in, paying, the product's core action, saving and backing up data, and the emails or webhooks other people rely on.
- Mark the parts of those paths you don't control, such as the payment provider, sign-in service, email sender, model API and open-source packages. That's where change arrives without a pull request of yours.
- Define success at the customer's outcome. "Checkout page loaded" tells you little; "payment confirmed and order marked paid" is the event that has to keep happening.
- Watch scheduled and rarely used work with heartbeats, and busy paths by counting successes against what's normal for that time.
For example:
| What has to work | Success signal | How to watch it |
|---|---|---|
| Nightly backup | Backup copied and verified | Heartbeat once a day, with an hour's grace |
| A rarely used flow | A scheduled synthetic run completes it | Heartbeat from each run, every 15 minutes |
| Sign-in | Successful sign-ins | Count per 15 minutes against the same time last week |
| Checkout | Orders marked paid | Count per hour against expected; alert on a sustained drop |
A flow that nobody uses overnight can't prove anything on its own, so a synthetic run with a test account goes through it on a schedule. Counting successes on busy paths catches partial breakage, which often never shows up as an error. If a button does nothing in one browser, the request never reaches your servers, so they have nothing to log.
The hard part is the threshold. A tight one fires on normal variation, and a loose one lets a partial break slip underneath. With the settings below, the break is never caught; raise the line to 85% and see what that costs before the deploy.
Simulated data. A deploy at 14:00 breaks some sign-ins without logging an error. The alert fires when two 15-minute intervals in a row fall below a share of the expected count.
The break is never caught
Successful sign-ins stay above the line all afternoon, even with the break. No false alarms from normal variation.
Show the simulated numbers
| Interval | Expected | Successful |
|---|---|---|
| 08:00 | 75 | 83 |
| 08:15 | 78 | 90 |
| 08:30 | 80 | 83 |
| 08:45 | 83 | 86 |
| 09:00 | 85 | 90 |
| 09:15 | 87 | 83 |
| 09:30 | 89 | 83 |
| 09:45 | 91 | 75 |
| 10:00 | 93 | 95 |
| 10:15 | 95 | 91 |
| 10:30 | 97 | 104 |
| 10:45 | 99 | 102 |
| 11:00 | 100 | 92 |
| 11:15 | 102 | 116 |
| 11:30 | 103 | 89 |
| 11:45 | 105 | 107 |
| 12:00 | 106 | 89 |
| 12:15 | 107 | 89 |
| 12:30 | 108 | 105 |
| 12:45 | 108 | 121 |
| 13:00 | 109 | 108 |
| 13:15 | 109 | 112 |
| 13:30 | 110 | 106 |
| 13:45 | 110 | 96 |
| 14:00 | 110 | 90 |
| 14:15 | 110 | 86 |
| 14:30 | 110 | 90 |
| 14:45 | 109 | 82 |
| 15:00 | 109 | 94 |
| 15:15 | 108 | 90 |
| 15:30 | 108 | 97 |
| 15:45 | 107 | 89 |
| 16:00 | 106 | 80 |
| 16:15 | 105 | 85 |
| 16:30 | 103 | 78 |
| 16:45 | 102 | 78 |
| 17:00 | 100 | 76 |
| 17:15 | 99 | 91 |
| 17:30 | 97 | 83 |
| 17:45 | 95 | 82 |
| 18:00 | 93 | 72 |
| 18:15 | 91 | 76 |
| 18:30 | 89 | 76 |
| 18:45 | 87 | 70 |
| 19:00 | 85 | 78 |
| 19:15 | 83 | 72 |
| 19:30 | 80 | 68 |
| 19:45 | 78 | 64 |
Requiring two low intervals in a row, as the simulation does, cuts false alarms. It adds at least 15 minutes, and more when a partial drop hovers around the line.
Start with whatever would hurt most if it stopped working tonight. Make it ping a URL when it succeeds, decide how long it can stay silent before that counts as broken, and send the alert to your phone. Then break it on purpose and check that your phone goes off.
Share this post
Related reading
Anam Assist: a talking avatar for Home Assistant
I built an unofficial Home Assistant integration that puts an Anam avatar in the browser. Anam runs the conversation; Home Assistant decides which tools it can use.
Reasoning effort in Claude Code and Codex: where max stops paying
On Artificial Analysis's Terminal-Bench runs, Opus 5.5 scores the same at xhigh and max but takes five times as long to start answering. My levels, and why.