Articles
August 2026

The Health Check That Was Lying to Me

Shelfie runs on a free Supabase project, and free Supabase projects go to sleep. So I wrote a small robot to prove the database was still being used. It ran green for eight days without ever touching the database.

Supabase pauses free-tier projects that go quiet for long enough. That is a perfectly reasonable thing for them to do, and a slightly awkward thing for Shelfie, which gets used in bursts. A run of heavy use while someone is planning meals, then nothing at all for a fortnight. Precisely the usage pattern that gets your database switched off.

The fix is a scheduled job that touches the database often enough to keep it awake, and GitHub Actions will run one for free. Fifteen minutes of work.

Fifteen Minutes, Four Commits

I wrote it four times in one sitting. The first version queried the books table directly, and row-level security blocked it, correctly, because a scheduled job has no user session. So I pointed it at the REST root instead, which wanted an API key. I added the key, and it still wasn't clean. So I switched to auth/v1/health, which is anonymously accessible and answered with a tidy 200.

Four commits in five minutes. Green tick. Done, I thought, and went and did something else for a week.

Eight Days of Being Wrong

A week later I noticed the weekly schedule had skipped a run. GitHub makes no promises about scheduled workflows firing on time, and will drop them entirely when its runners are busy, so I moved the job from weekly to daily. A missed trigger would no longer matter. Still green.

Then I opened the Supabase dashboard and found the project had been pausing anyway.

auth/v1/health checks GoTrue, which is Supabase's authentication service. It is a different system from the Postgres database, sitting behind the same domain name. Every morning for eight days, my workflow had asked an honest question and received an accurate answer about something I did not care about. The green tick was true. It simply wasn't relevant.

That is the part worth keeping. A health check that doesn't touch the thing you actually care about isn't a weak check, it's a decorative one, and because it passes it is worse than having no check at all. No check leaves you knowing you aren't monitoring something. A passing irrelevant check leaves you convinced that you are.

The Two-Minute Bug

The real fix was to query an actual table using the service role key, which bypasses row-level security the way a scheduled job needs to. I wrote it, pushed it, triggered it by hand, and it failed instantly with a 404.

The workflow had asked for a table called books. Shelfie's tables are cookbook_books and cookbook_recipes.

Two minutes later it was fixed. Set that against eight days for the previous bug. The second one wasn't easier or more obvious. The difference was entirely that the broken version failed loudly, and the useless version passed quietly.

The Bug Behind the Bug

I didn't invent the name books out of nowhere. Shelfie's own CLAUDE.md, the file that tells Claude Code how the project is laid out, said the tables were called books and recipes. They had been renamed at some point and the documentation had never caught up.

So the workflow was written correctly against a map that was wrong. Three minutes after fixing the table name I fixed the docs, and that is the only commit in the whole sequence that stops the same mistake happening again. Stale documentation in an AI-assisted project isn't just untidy. It is an instruction, and it gets followed.

What It Looks Like Now

Twenty-three lines of YAML. It runs daily, requests a single id from a real table with credentials that can genuinely read it, and fails the build on anything that isn't a 200. Seven commits and eight days to arrive at something that short, which feels about right for infrastructure.

The database has stayed awake since.