For a while every self-hosted service in my homelab had its own login form. Eight services, eight password prompts, eight places a weak password could hide. That is not security, it is friction pretending to be security.
Authelia fixed it in about two hours. This is what it does, why I chose it over the heavier option, and the one class of service I deliberately left outside it.
How it works
Authelia sits behind Traefik as a ForwardAuth middleware. Every request to an internal service gets checked against Authelia first. One login sets a cookie scoped to my internal domain, and every service under that domain trusts the cookie. Log in once, you are through to all of them. TOTP is enforced for anything in the admin group, so the sensitive services always ask for a second factor.
The internal domain here is hm.example.com, a documentation placeholder. The real one is a private split-horizon domain that only resolves inside the house. The shape is what matters: one parent domain, a wildcard cookie, and every service living as a subdomain under it.
The default that matters
The design decision worth copying is the default. Authelia's access control starts at deny, and every service I want reachable is an explicit allow rule. Adding a service means adding a line. Forgetting to add a line means the service is locked, not open. That is the failure mode I want. A system that fails closed is one you can extend without lying awake wondering what you left exposed.
Why not Keycloak
People reach for Keycloak here, and for an organisation with real identity needs it is the right tool: federation, user self-service, a full admin console. For five users it is a lot of moving parts to run and patch. Authelia is a single Go binary with a file-based user database, argon2id password hashing, and a SQLite session store. Sessions expire after five minutes idle, one hour hard cap, with a one-month "remember me" for trusted devices. It does less, and for this scale that is the point.
The service I left outside
Forward-auth returns HTML login pages. That is fine for a browser, and wrong for a sync client. The Nextcloud mobile app, CalDAV, CardDAV, and git over SSH all speak their own auth on every call and have no idea what to do with a redirect to an HTML login form. Put them behind forward-auth and they fail silently.
So those services stay outside Authelia and lean on their own authentication: Nextcloud's brute-force throttle and app passwords, git's SSH keys. The rule I follow is to match the auth pattern to what the client can actually handle, rather than wrapping everything in the same layer because it feels more secure.
What it comes back to
Single sign-on for a small system is a one-day project, not a quarter-long identity programme. One gatekeeper, a deny-by-default policy, TOTP on the admin group, and a short list of services that stay outside because their clients cannot speak the protocol. That is enough to stop running eight separate password forms, which was the whole point.

