Skip to main content

The foundation, or: why I wrote more documents than code

Author
Thornbush
HomeStack Part I - This article is part of a series.
Part 4: This Article

Why
#

Before a single service was running, I had a database strategy document. A folder structure decision record. A Git governance policy. A backup philosophy. And a growing sense that I was doing the exact thing I spend my professional life warning clients about: confusing documentation with progress.

There is a legitimate reason for this, and an embarrassing one. The legitimate reason is that decisions made at the foundation are expensive to undo. Choosing the wrong database engine in week one means migrating everything in week ten. The embarrassing reason is that writing documents feels productive without the risk of anything actually breaking.

Eventually I accepted the contradiction and leaned into it. The documents would serve a real purpose — not as governance theatre, but as a forcing function. Every architectural choice would be argued, not assumed. Every exception would be named and justified. If I couldn’t explain why, I couldn’t proceed.

That discipline turned out to matter more than I expected.

Family
#

The server was running. Nothing visible had changed in the house. My partner hadn’t noticed yet — or was being polite.

The first thing I wanted to show was a password manager. Not because it was technically impressive, but because it was immediately useful: one place for all the passwords the family had been scattering across browsers, sticky notes, and memory. A shared vault. Something real.

It took longer than expected. But when it finally worked — HTTPS, no browser warnings, a proper login — I called my partner over. The reaction was cautious but genuine. What is it? A password manager. For all of us? Yes. And it’s running on that thing in your office? Yes. Hm.

That was enough.

Tech
#

The core architectural decision of this phase was straightforward and has held ever since: one database engine for everything. PostgreSQL, everywhere, always. No exceptions — except the two exceptions, both documented.

Uptime Kuma doesn’t support PostgreSQL. It uses SQLite. The pragmatic choice was to accept the exception, name it, and move on. Seafile — chosen over Nextcloud after a benchmark that made Nextcloud look embarrassing — requires a MySQL-compatible database. MariaDB became the second exception. Both are isolated. Neither contaminates the rest of the stack.

The larger lesson was about the difference between principles and rules. A rule says PostgreSQL always. A principle says: be consistent, be recoverable, be honest about trade-offs. Uptime Kuma with SQLite serves the principle. It violates the rule. I wrote a decision record for it anyway — which forced me to articulate why the exception was justified instead of just letting it slide.

SMTP was harder. Getting Authentik to send email through Proton Mail required mounting certificates into Alpine containers, debugging Django’s TLS verification, discovering that the Docker Compose entrypoint field does not behave the way you assume, and spending an evening on a problem that a large organisation would have assigned to a specialist. Eventually it worked. The test email arrived. It felt disproportionately satisfying.

Project
#

Seven days. Vaultwarden, Caddy with a self-signed Root CA, Authentik with a full upgrade path from 2024 to 2026 in one session, CrowdSec at the network edge, Trivy for container scanning, BorgBackup extended to cover the new stack, and a cron framework to tie it together.

The Authentik upgrade deserves a note. The version I had installed was more than two years behind the current release. Database migrations require going through every major version in sequence — no jumping. So I did: twelve upgrade steps, each one a docker compose pull && down && up, each one verified in the browser before proceeding. It took an afternoon. It was tedious and completely correct.

CrowdSec asked a question I keep returning to. Installation took under an hour. The firewall bouncer was active. The home network was whitelisted. Fail2Ban was already running. Within a day of setup, the stack had more active threat detection than many of the organisations I advise professionally.

I don’t think this reflects well on those organisations. I think it reflects how low the baseline is, and how much of enterprise security theatre consists of complexity that protects budgets more than systems.

The foundation was closed. Everything that came next would build on something solid.

HomeStack Part I - This article is part of a series.
Part 4: This Article