In the reverse proxy post the stack already had access, names, TLS, and HTTP routing. But https://grafana.lan without login is an open door on the LAN (and sometimes beyond it if something failed at the perimeter).
The fourth layer answers “who are you?”: application identity, OIDC, forward auth. The VPN from post 006 answers “are you on my network?”; DNS from 007, “what’s it called?”. Mix them up and you stand up an IdP when the problem was reaching the NAS, or you expose Grafana to the internet when a tunnel would have been enough.
I start from the RPS principle and the bridge/anchor axis applied to homelab identity. The real case is Zitadel (just getting started, not pretending years in production) and the thread continues from digital sovereignty and cloud-first.
The question after the proxy
Once you have a name, a certificate, and routing, the temptation is to lock down every panel now. Basic auth in Caddy, an Authelia container, or a full IdP with organizations, projects, and policies. All three close a login, and each one assumes a different problem.
Authelia and OAuth2-Proxy do forward auth: the proxy asks a service whether the request may pass. A doorman at the edge, no central directory of applications. Keycloak, Authentik, and Zitadel are an IdP: apps speak OIDC or SAML to you. That difference matters more than the product name.
In a personal homelab the order is still the one from this series:
- Network access: Tailscale, WireGuard, or OpenVPN
- Name resolution: sslip.io, MagicDNS, or Pi-hole
- TLS termination and HTTP routing: Caddy
- Application identity ← here
Jumping to 4 without the rest is scissors too early: platform identity for a service that doesn’t even have a stable name yet.
Two dimensions
| Dimension | Question |
|---|---|
| RPS | How much do you invest in identity architecture? |
| Bridge / anchor | How much optionality do you keep to migrate tomorrow? |
The same decision can be strategic stone and a strong anchor. Social login as the only factor is the usual example: zero ops today, the identity factor isn’t yours tomorrow.
RPS spectrum: self-hosted tools
Stone: protect services, now
Basic auth in Caddy or nginx (htpasswd) is the minimum stone: a file, a realm, and the browser asks for a username and password. Fine for a panel only you use. It doesn’t scale to several apps with SSO, 2FA is poor or missing, and the day you share the homelab with someone else you start to hate the password list.
Authelia (and OAuth2-Proxy) step up without making you an IdP administrator. Forward auth and 2FA in front of the proxy. The honest case is a personal homelab, few users, and services that don’t really speak OIDC (or that you don’t need to speak it). If Grafana, the NAS, and Home Assistant only need “don’t let just anyone in on the LAN”, Authelia is usually enough.
Paper: serious IdP, controlled risk
Keycloak is corporate paper: Java, heavy, predictable, the industry reference. You pick it when failure is expensive (several users, apps that already expect OIDC, someone else depends on login) and you accept running a server that isn’t light.
Authentik fills the same strategic slot with a different balance: full IdP, more modern DX, heterogeneous stack (Python and auxiliary services). More comfortable to live with in a homelab or a small team than Keycloak; it’s still paper. You stand up a directory, flows, and you own it when it falls over.
Same RPS strategy. Different price in RAM, learning curve, and how much it feels like an enterprise product.
Scissors: identity as a platform
Zitadel is born as a cloud-native IdP: event sourcing, multi-tenant by design, written in Go. In a homelab that shows up in the footprint (lighter than Keycloak, more compact than Authentik) and in the mental model: organizations, projects, applications, policies. Grafana becomes one more client.
It makes sense when identity is the core: several real OIDC apps, SSO you want to own, or a project (a PWA, your own SaaS) that will live for years with those tokens. The risk is the usual scissors risk: overengineering if you’re one person and three services behind forward auth.
False paper
Standing up Keycloak or Authentik when Authelia would have done. Standing up Zitadel when the problem was an htpasswd. It looks like paper (there’s an IdP, a panel, dense docs) but you pay complexity the context doesn’t ask for. Same pattern as cloud-first in miniature: the “serious” tool as the default.
Managed anchors: by depth
Not everyone self-hosts the IdP. The managed spectrum is a different scale; better not to mix it with the one above:
Shallower ────────────────────────────────────► Deeper
Social login BaaS auth Cloud IdP Hyperscaler IAM
(Google/GitHub) (Firebase, (Cognito, Auth0, (IAM + cloud stack)
Supabase) Clerk)
| Layer | Typical RPS | Reversibility |
|---|---|---|
| Social login only | Tactical stone | Anchor in the social provider |
| Firebase / Supabase Auth | Stone (MVP) | Medium anchor depending on coupling |
| Cognito, Auth0, Clerk | Paper (auth without ops) | Strong anchor |
| Hyperscaler IAM | Enterprise paper | Maximum anchor |
Supabase can be a bridge if you stay on Auth and standard Postgres; an anchor if the whole product lives there. Firebase anchors earlier. Cognito and Auth0 take ops off your plate and sell you an expensive exit.
Social login
“Sign in with Google” and nothing else is pragmatic stone: zero ops, the users already exist. It’s also an anchor: you don’t control the identity factor. If Google shuts the API, changes consent, or blocks your project, login dies and there’s no directory to fall back to.
The nuance I care about is different. Your own IdP (Zitadel, Keycloak, Authentik) with Google as an identity broker. Apps speak OIDC to you; you federate with Google. A partial bridge: tomorrow you drop Google and keep local users, or add another IdP, without rewriting every client. The anchor stays on the broker, not on each application.
My experience with Zitadel
I’ll say it without posturing: just getting started. First impressions.
I stood up Zitadel behind Tailscale, with MagicDNS for names and access. The perimeter is still the tailnet from post 006; the IdP isn’t open to the internet. The double anchor:
- Tailscale coordinates the network: operational bridge, anchor in the control plane unless you move to Headscale.
- Zitadel holds users and issues OIDC on an instance I run: a bridge if you stay on open standards.
Conscious anchor. The alternative I avoid is opening everything to the internet and handing login to Cognito “because that’s the professional choice”.
The setup is conceptually dense. Organizations, projects, applications, redirect URIs, who the issuer is. An htpasswd doesn’t prepare you for that map. IAG agents in the CLI cut the cost of the first configuration (the compose file, the Caddyfile, the silly Postgres error) and leave the mental model untouched. The agent leaves you a YAML that starts; understanding why an app doesn’t get the claim you expected is still on you. In SDD I talk about that same gap between artifact and judgment. The operational case, a real agent against the homelab, is another post.
What I noticed right away: Go shows. Zitadel takes less than Keycloak (Java) and less than Authentik (Python plus companions). On a mini PC already running Pi-hole and Caddy, you feel it.
I would start from the Zitadel docs with Caddy and the stack you already have. Domains follow the same progression as in post 008: sslip.io with a self-signed cert to try things, DynDNS if you need it, a real domain and Let’s Encrypt when the name is worth keeping. Pi-hole and ACME renewals are already covered there; the IdP doesn’t invent a different network physics.
When to use what
Basic auth or Authelia is enough when:
- It’s you, or a household, and the services don’t speak OIDC.
- The problem is keeping strangers out, not federating five applications.
- You don’t yet know whether the homelab stays.
Keycloak or Authentik when:
- You already have several OIDC apps and shared login is the real problem.
- You want an IdP many people have operated before, at the cost of weight (Keycloak) or stack (Authentik).
Zitadel when:
- Identity is part of the system you’re building, not a padlock in front of Grafana.
- You care about footprint and you’ll live with a platform model rather than a doorman.
- You can live with first impressions: the tool is young in your stack, not in the market.
Stay on social login or a BaaS when the product is still an MVP and the cost of running an IdP outweighs an anchor you’ve already named.
Honest limitations
- An IdP doesn’t replace the perimeter. Zitadel without a VPN (or with 443 open to the internet) is another surface, not more sovereignty.
- Misconfigured OIDC is worse than
htpasswd. A loose redirect URI or a misunderstood public client opens more than it closes. - Scissors feels great the first weekend. Three months later, if you only have three services, Authelia would have left you more time for the hobby.
- I’m writing from the first setup, not from years of running this in earnest.
What comes next
With this layer, the stack I started in access is named: network, names, TLS, identity. Each answers a different question. Mix them and you use Caddy to authenticate users or Tailscale to replace OIDC.
The next thread leaves the rack: how you govern what the agent writes (SDD) and, later, how that same agent helps you operate the homelab without replacing judgment.
Zitadel, Keycloak, or Authelia only make sense in your context. The useful question is which strategy you’re playing and which anchors you accept.