The copy pass from last week had been waiting on the local server for me to look at it before it went anywhere. Today I looked, shipped it, and then spent the rest of the session on a part of the site I'd been quietly unhappy with: the Lab.
Shipping What I'd Sat On
The review went quickly because the work held up. The rewritten About, the smaller project cards, the status pills that finally jump to their section — all of it read the way I wanted on screen, so it went to production. Before pushing I scanned the whole change for anything that shouldn't be public, found nothing beyond the contact email that's meant to be there, and let it deploy.
One thing was inconsistent. The homepage cards had been collapsed down to a title and a Details button, but the four portfolio subpages still showed the old full-length versions. I matched them to the homepage card, with one deliberate difference: on the subpages the details start open. A page you reach by clicking "view full details" shouldn't then hide the details behind a second click. Same component, same styling, just expanded by default, and still collapsible if you want. While I was in there I caught a dead link on the completed page — the homelab card pointed at an anchor that didn't exist — and sent it to the right place.
The Lab Was Performing for the Wrong Audience
The Lab section had always sat awkwardly. On the homepage it was a long wall of components and a couple of service tiles, written like a pitch. But the lab isn't a pitch. It's the thing I actually use, and most of what runs on it has no business being advertised to a recruiter reading my portfolio.
So I took it apart. The homepage Lab section is gone, which leaves a cleaner run from Portfolio to Blog to Contact. The footer lost its "Lab Services" column, and the loud gradient card that announced the homelab is now a single quiet line with a small link. The point was to stop selling the lab and let it go back to being mine.
A Launcher, Not a Showcase
The rebuild of the /lab page is where the real thinking went. What I wanted was simple: one page that gives me one-click access to everything I run, with some live numbers from the host. The problem is where that page lives.
This repo is public on GitHub, and the site is indexed. If I list every container with its address, I'm committing a map of my whole setup to a public record that never really goes away, and pointing at services that aren't meant to be reachable from outside anyway. The honest answer is that a personal launcher belongs on a private dashboard inside the network, not on a public site. For now I took the safe middle: the page only links the handful of services already exposed through the Cloudflare tunnel and already behind their own logins. Nothing internal, no home network addresses, nothing that wasn't already public.
The stats panel pulls real Docker counts — stacks, containers, images, volumes, networks — and I wired them into the same commit hook that already refreshes the homepage numbers, so they stay current on their own. I wanted live graphs from Grafana too, but Grafana isn't public, and putting it online just to decorate a page is the exact mistake I'd just talked myself out of. The counts and the hardware specs are the honest version until I stand up a proper private dashboard.
By the end it was all live: the copy, the matched subpages, the stripped-back Lab, and the new launcher, confirmed on the real domain.
Catching the Blog Up, and Building the Missing Piece
With today's entry written, two loose ends on the blog were worth closing. The June 13 entry had been written but never turned into a page, so the public blog jumped from June 12 straight to today, missing a day. Publishing it exposed the bigger problem: there was no tool doing the conversion. Every post page so far had been built by hand, one at a time, which is exactly why one of them had quietly never been built at all.
So I wrote a small script to do the job properly. It reads the journals, turns each one into its page, rebuilds the index, and keeps the previous and next links pointing the right way. No dependencies, nothing clever, just the repeatable version of what I'd been doing by hand and occasionally forgetting. Adding a post is now three steps: write the journal, run the script, push. This very update is the first time the loop has run on itself, which is the only real way to know it works.
What's Next
- Decide whether Lab still belongs in the main nav now that it's a personal page, or should only be reachable from the footer
- Stand up a real self-hosted dashboard inside the network for the full container list, with live Grafana panels
- Wire the blog script into the commit flow so a new journal publishes without the manual step
- Decide whether the portfolio subpage cards stay open by default or collapse to match the homepage
The lesson is about a page trying to serve two people at once. The Lab wanted to be both a thing I show off and a tool I use, and it did neither well. Splitting them — keep the showing-off in the portfolio, make the lab page a plain launcher — was the fix, and the only honest place to draw the line was what's already public. The rest waits until it has a private home to live in.