There’s a specific kind of tired that comes from typing the same username and password into a fresh machine for the tenth time. My homelab is small — a mini PC running a handful of containers, a reverse proxy box, a NAS, a desktop — but it keeps growing, and every new container meant another local account, another password, another thing to keep in sync.
So I did what any engineer with a free weekend does: I decided to fix it properly. One identity, everywhere.
It lasted about a day.
This post is about why I built it, why I removed it, and what I think the episode says about decisions — at home and at work.
The plan
The requirements were reasonable:
- One source of truth for my user, across physical hosts and containers.
- Web single sign-on for the services I use every day: media server, dashboards, monitoring.
- A break-glass local account on every host, in case the central system ever failed. This one was non-negotiable: an identity system that can lock you out of your own machines is worse than no identity system.
I compared four options — Authentik, Authelia, FreeIPA and Kanidm — and picked Kanidm: a single Rust binary with an embedded database, native OIDC for the web side, and a native PAM/NSS daemon for logging into the hosts themselves. One directory, two protocols, very little to operate. FreeIPA felt sized for an organisation, not a house. Authelia would have needed a separate directory anyway. Authentik was a close second, just heavier.
I wrote down a staged rollout, too: pilot on the monitoring dashboard (low risk, native OIDC), then host login on a single machine with the local account left intact, then extend one host at a time. Apps without OIDC support would sit behind a forward-auth proxy at the reverse proxy.
On paper, it was a good plan. It was even a careful plan.
The day of real use
I had it running the same day. And then I used it.
- Only a couple of my services could actually do real SSO. The rest could only be put behind a login gate — which meant logging in to the gate, and then logging in to the app. Two logins instead of one. The opposite of the goal.
- The local accounts on each host couldn’t really go away. Removing them safely would have meant touching PAM, NSS and every assumption baked into each machine — for a household of two.
- And in exchange I had acquired a directory server, an auth proxy, new reverse-proxy patterns and modified login stacks on the hosts. All of them new things that could break, and that I’d need to understand at 11pm when they did.
My note to myself the next morning, roughly translated: “This centralized identity thing has been a fiasco. Most services can’t use it, for some it’s an extra layer but they still need their own login, and you can’t remove the local user from each machine without making a mess.”
So I removed it. Containers deleted, packages purged, config files restored, every service checked afterwards. The whole teardown took less time than the build.
What replaced it
Something much simpler: the home network is the trust boundary. A device on the main home network is trusted and reaches services directly. Apps keep whatever login they ship with, on their own merits. Guests use a separate network.
That’s not a universal answer — it would be the wrong answer for a company. But it’s the right answer for this threat model: a house, two people, no public exposure. I’d been designing for a threat model I don’t have.
The uncomfortable part
Here’s what I find most interesting in hindsight: the limitations that killed the project were written down before I built it. My own design notes said which apps would only get a gate instead of real SSO, and that the local accounts couldn’t really be replaced. I knew. I built it anyway.
Why? Momentum, mostly. The design was elegant, the comparison of tools was satisfying, and once a plan feels “done” there’s a pull to execute it. A careful plan can make you more committed to a bad idea, not less, because the care itself feels like validation.
I’ve seen exactly this at work, and I’ve done it at work. A team writes a thorough design doc, the risks section is honest, everyone nods — and the risks section is never read again, because the decision to build was really made before the doc was written.
What I’m taking from it
Make the risks section a set of exit criteria. “Most apps won’t support OIDC” shouldn’t be a caveat; it should be a question with a deadline: if, after the pilot, fewer than N services get real SSO, we stop. Then stopping is just following the plan, not admitting failure.
Pilot on the hard case, not the easy one. I piloted on the service most likely to work. The project was decided by the services least likely to. Testing the happy path first felt prudent; it actually postponed the real question.
Cheap to build means cheap to discard — use that. Part of why this was easy to walk away from is that it cost a day, not a quarter. The faster you can build something, the more willing you should be to treat it as an experiment rather than a commitment. That’s true for teams too: shorten the loop, and reversals stop being dramatic.
Fewer moving parts is a feature. Every service I didn’t end up running is one less thing to patch, monitor, back up and keep powered on. Simplicity isn’t just less work; it’s less infrastructure, less energy, and fewer ways for a home to stop working when the person who built it isn’t around.
Reversing fast is a skill, not a failure. The most valuable thing I did in this whole project was the teardown. I’d like to be the kind of manager who makes that easy for a team to do — where “we built it, it doesn’t pay for itself, let’s remove it” is a normal sentence in a retro, not a confession.
The homelab is back to being boring. I like it better that way.