Moving sign-in to authentik, one consumer at a time

Everything private I run sits behind a sign-in. That used to be Authelia; now it is authentik, on its own small cloud machine, id. I wrote about the plan when the move started. This page is where it stands, as of mid-October 2026.

The problem

Authelia checked sign-ins well, but it lacked the management side: creating accounts, inviting someone, letting a person recover their own account. Replacing it carried its own risk. The sign-in service sits in front of everything, so one mistake could lock the household out of all of it at once.

What I built

  • authentik on its own machine, id, beside Authelia, with its own PostgreSQL database and an outbound Cloudflare Tunnel. It got a new hostname, so nothing had to be re-pointed twice.
  • Identity configuration in Git, as blueprints: the groups, the accounts without passwords, the sign-in page’s brand, and every application and provider. Client secrets come from the environment, so none is in the repository.
  • A second factor for everyone, required at first sign-in: a passkey or an authenticator app.
  • One change per service, each with its own rollback, and each gated on something I could read rather than a date.

How it works

As of mid-October 2026, these have moved to authentik: the household’s sign-in to Cloudflare Access, the reverse proxy’s forward-auth on cs and on wp, the budget app’s and Nextcloud’s own OIDC logins, and the docs site’s own gate. Authelia still runs until the last step retires it.

Access policies that admit through authentik require membership of a household group, not just a successful sign-in. An account outside those groups is refused at the edge. A guest account ends on a date set for that account: a policy on the Access login refuses it after that day, and revoking one was proven on a test account before the first real guest.

One design problem surfaced late. authentik’s forward-auth, in its domain-wide mode, decides per cookie domain, not per host. Two services needed a different group rule from the hosts beside them under the same domain. Each got its own single-application outpost: a small container next to the proxy on its host, with a policy of its own.

The first blueprint apply taught two things. A !KeyOf reference resolves only inside its own file, so references between files use !Find instead. And a blueprint’s errors aren’t shown on the blueprint: they’re in the logs of the task that applied it.

Decisions and trade-offs

  • Run both side by side. A single-night cutover would have been faster. Running authentik beside Authelia let each service move, and move back, on its own.
  • No password import. authentik has no importer for Authelia’s password hashes or second factors, and writing them into its database by hand would be a hack. Every account got an invitation instead and set its own password and second factor.
  • Gates on readings, not dates. Each step waited on something observable. Authelia retires only when its login log shows no successful sign-in over a span of normal use.
  • Honest about the cost. One remote identity provider still means the home network fails closed when the internet or id is down. authentik is heavier than Authelia and has to be upgraded one major version at a time. What it buys is an admin interface, invitations, self-service recovery, an event log and per-application rules.

What’s next

The last step retires Authelia and its machine, once its login log reads empty over a span of normal household use.

Built with

authentik, PostgreSQL, Cloudflare Access (generic OIDC), Cloudflare Tunnel, Traefik forward-auth, Docker Compose, DigitalOcean.