I spend more time than I'd like to admit copying an IP address out of the Hetzner console before every SSH session. So one evening I built myself a small, ugly, owner-only page — and it ended up doing a lot more than save me a few clicks.
Added a personal /ops dashboard (gated so only my account can even load the route) that pulls a few things I check by hand into one place — server status, database health, email activity. The database-health panel surfaces Supabase's own performance advisor, and the first time I opened it, it was sitting on 26 row-level-security policies re-evaluating auth.uid() on every single row instead of once per query, plus a handful of duplicate policies and indexes nobody had noticed. All fixed the same afternoon.
Why bother
None of this was broken in a way anyone would file a bug about. The app worked. Checks ran. Alerts sent. But "works" and "efficient" are different claims, and I only ever look at the second one when something forces me to — which, for a one-person team, is basically never, because there's no coworker whose job it is to ask "hey, did you check the advisor tab lately?"
So instead of relying on remembering to check, I built the check into something I'd actually open: a page that shows me the Hetzner box's CPU/disk/network graphs (so I stop tab-switching to a console just to see if the thing is on fire), a rollup of recent email activity, and — the part that actually mattered — Supabase's security and performance advisors, rendered as a real list instead of a number I have to go look up separately.
What the advisor actually found
The performance advisor's single biggest finding was 26 RLS policies across almost every table — monitors, incidents, profiles, uptime_logs, and half a dozen others — all shaped like this:
-- What every one of these policies looked like
create policy "Users can view own monitors" on monitors
for select
using (auth.uid() = user_id);That reads perfectly reasonably, and it's the version every Supabase tutorial shows you. The problem is what Postgres actually does with it: auth.uid() gets re-evaluated once per row the policy is checked against, instead of once for the whole query. On a small table that's noise. On a 200-row page load it's fine. On the tables that grow without bound in this app — uptime check history, incident logs — it's a cost that scales with data you're specifically hoping accumulates.
The fix is one extra pair of parentheses:
-- Same policy, wrapped so Postgres treats it as a stable value
create policy "Users can view own monitors" on monitors
for select
using ((select auth.uid()) = user_id);Wrapping the call in (select ...) doesn't change what the policy allows — it changes how Postgres plans it, letting the value get computed once and reused instead of recomputed per row. It's Supabase's own documented fix for this exact lint, which is presumably why their advisor knows to flag it by name.
Alongside that, the advisor turned up two smaller things that were pure debt rather than a subtle tradeoff:
- Three duplicate policies on the
monitorstable — "Users can insert own monitors" and "Users can insert their own monitors," identical logic, both still active. Leftover from a rename at some point that added the new one without removing the old. Harmless, but Postgres was evaluating both of them, every time, forever. - Two identical unique constraints on a rollup table — same columns, same uniqueness guarantee, backed by two separate indexes doing the same job.
A green dashboard tells you the thing works. It doesn't tell you whether it's still working the way you'd choose if you looked today.
What I didn't touch
The advisor also flagged a batch of indexes as "unused" — meaning Postgres has recorded zero scans against them since stats were last reset. I left every one of them alone. This app is a few months old and still low-traffic; an index sitting idle right now usually just means the query pattern it exists for hasn't been hit hard yet, not that it was a mistake to add. Dropping it to clear an informational warning, only to need it back the moment real load shows up, is churn with no upside. "The advisor flagged it" isn't the same question as "should I actually change this" — the dashboard's job is to surface the question, not answer it for me.
The actual lesson
None of this was a bug. Nothing was down. Nobody would have ever opened a support ticket about query-plan efficiency on a RLS policy. That's exactly why it sat there for weeks — there was no external pressure forcing me to look. The fix wasn't smarter code, it was building a smaller mirror: one page that shows me the boring, unglamorous health signals I'd otherwise have to remember to go check four different places for, on four different logins, none of which I do for fun.
Cheapest tool I've shipped in a while, and it paid for itself before I'd even finished writing it.