I Built a Dashboard for the Things I Actually Check

Written by

in

Five separate logins became one page. The interesting part was how often I turned out to be wrong.

I check five things regularly: whether the homelab is healthy, my portfolio, my health trend, whatever trip is coming up, and my to-do list. Each one lived somewhere different. Each had its own login. None of them talked to each other. I have to go looking for information by opening a browser, going to my bookmarks page and finding the right tile to click:

My Heimdall Dashboard of links to my self-hosted services.

Information you have to go and fetch is information you check less often than you should.


What It Looks Like Now

One page. Five modules, each running in its own container, pulled together by a dashboard shell called Glance. The shell is configuration, not a product — it reads each service’s API and renders the numbers. That’s the whole trick (admittedly greatly helped by LLMs to design):

The architecture for my data dashboard

The health module was the biggest piece by some distance. I exported four years of Fitbit history and imported it alongside live Garmin data, so the trend line runs continuously from March 2022 to today instead of restarting when I changed watches. About 7 million data points. There’s a real gap of six days in January where neither device recorded anything, which is the week I switched. I’ve left it in. It’s honest.

The finance module pulls positions from my broker and reconciles them against manually entered accounts for the things a broker doesn’t know about — Provident Fund, cash, the rest. Everything now sits behind a single login.


Three of My Four API Guesses Were Wrong

This is the part worth writing down.

I planned the whole thing on paper first (with the help of AI, of course, including this post), including which endpoint each module would call and which fields it would read. When I came to build it, three of the four were wrong.

The monitoring tool returned a flat structure where I’d assumed a nested one (I needed the LLM to explain this to me too!). The finance app had moved its performance endpoint to a new API version, and the old path returned a 404. The travel app used different field names to what I was expecting.

None of these would have crashed anything. Each would have quietly rendered an error tile on a dashboard I’d then have trusted. The fix was boring and it worked: read a live response from every endpoint before writing a single line of template code.


The Service I Decided Not to Build

The last gap was authentication. Several services were reachable on my network with no login at all, and the obvious answer was to put single sign-on in front of everything.

So I drafted the whole build. Container, secrets, rollout order, backups. Then I actually tested my assumptions and found that most of the services I’d listed as unprotected were, in fact, protected. The real list was less than half of what I’d written down (i.e. in my issue backlog in my Obsidian Vault). A lot of the services were things I wanted open so I could use them on the fly on an ipad or my phone.

I closed the gap with tools already running instead. I stopped proxying my router’s admin interface entirely rather than putting a login in front of it, and turned off open registration on one app that had it. That took an afternoon and added nothing new to maintain.

A new service is a permanent cost. Backups, updates, secrets to store, another thing that breaks at 2am. Since I already stayed up late last night to figure out why Jellyfin was transcoding when no one was using it, I figured “Simplex sigillum veri“.


What I’d Tell Someone Starting

Verify before you build. Every assumption I wrote in my .md vault was cheap to check and expensive to get wrong.

Back up the databases properly. A snapshot of a running database is a snapshot of a database mid-write. Every module here dumps itself fifteen minutes before the nightly backup, and each dump refuses to complete unless it passes an integrity check. A backup you haven’t verified is a hope.

And scope the problem before you scope the solution. I nearly built an identity provider to solve a problem that turned out to be four services and a router.

Comments

Leave a Reply