Single Sign-On for the Homelab with Authentik

One login for every self-hosted service, with real MFA -- instead of a different password (or no password at all) guarding each one.

What you'll build and why

Authentik is a self-hosted identity provider: one login, with real multi-factor authentication, that other services trust instead of each managing its own username/password database. This guide sets it up and connects one OIDC-native app and one app that has no built-in SSO support (via Authentik's forward-auth proxy, which sits in front of it and checks authentication before requests ever reach the app).

Don't build this if: you're only running one or two services -- the setup and maintenance overhead of running an identity provider isn't worth it until you're managing logins across enough services that "one login for everything" is a real, felt improvement.

How it works

  You
│
▼
Reverse proxy (prerequisite guide)
│
├──► App A (OIDC-native, e.g. supports "Login with
│     OIDC" directly) -- redirects to Authentik,
│     gets a token back, trusts it
│
└──► App B (no SSO support) -- sits behind
Authentik's forward-auth proxy provider,
which checks with Authentik before any
request reaches App B at all
Authentik itself: holds the actual user accounts,
enforces MFA, issues tokens/session cookies

For OIDC-native apps, Authentik and the app talk directly (a standard protocol handshake). For apps with no SSO support, forward-auth is the workaround: your reverse proxy asks Authentik "is this request authenticated?" before passing it through, so the app behind it never has to know SSO exists.

Before you start

Decision: Authentik or Authelia? Both are legitimate, actively maintained self-hosted identity providers. Authentik has a fuller-featured web UI for managing users/apps/flows and broader native protocol support (OIDC, SAML, LDAP); Authelia is lighter-weight and configuration-file-driven rather than UI-driven, often paired directly with Traefik. This guide uses Authentik for its more complete UI-driven setup, which is friendlier for a first SSO deployment -- Authelia is a reasonable alternative if you prefer config-as-files over a web UI.

Steps

Step 1: Run Authentik

services:
authentik-server:
image: ghcr.io/goauthentik/server:latest
command: server
environment:
AUTHENTIK_SECRET_KEY: "<generate-a-long-random-string>"
AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
AUTHENTIK_POSTGRESQL__PASSWORD: "<POSTGRES_PASSWORD>"
AUTHENTIK_REDIS__HOST: authentik-redis
ports:
- "9000:9000"
depends_on:
- authentik-postgres
- authentik-redis
restart: unless-stopped
authentik-worker:
image: ghcr.io/goauthentik/server:latest
command: worker
environment:
AUTHENTIK_SECRET_KEY: "<same-value-as-server>"
AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
AUTHENTIK_POSTGRESQL__PASSWORD: "<POSTGRES_PASSWORD>"
AUTHENTIK_REDIS__HOST: authentik-redis
depends_on:
- authentik-postgres
- authentik-redis
restart: unless-stopped
authentik-postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: "<POSTGRES_PASSWORD>"
POSTGRES_USER: authentik
POSTGRES_DB: authentik
volumes:
- "authentik_postgres:/var/lib/postgresql/data"
restart: unless-stopped
authentik-redis:
image: redis:alpine
restart: unless-stopped
volumes:
authentik_postgres:

Authentik ships its own current official Compose reference -- check goauthentik.io's installation docs for the exact current environment variable set and image tags before deploying, since this stack has real service dependencies (Postgres, Redis) that need to match.

Step 2: Complete initial setup

Visit http://<host-ip>:9000/if/flow/initial-setup/, create the initial admin account. Put this behind your reverse proxy with a real domain and HTTPS before doing anything further (see the prerequisite guide).

Step 3: Enable MFA on your admin account

In Authentik's own UI: your user settings, add a TOTP authenticator (or a hardware key, if Authentik's current version supports it in your setup). Do this before configuring anything else -- the account managing every other service's auth should not itself be single-factor.

Step 4: Connect an OIDC-native app

In Authentik: Applications → Providers → Create, choose OAuth2/OpenID Provider, configure the redirect URI per that specific app's own OIDC setup instructions. Then Applications → Applications → Create, linking the provider to a new Application entry. In the target app itself, configure it to use Authentik as its OIDC provider (client ID/secret from Authentik's provider config).

Step 5: Put a non-SSO-aware app behind forward-auth

In Authentik: Applications → Providers → Create, choose Proxy Provider, forward-auth mode, pointed at the internal app. In your reverse proxy's config for that app's domain, add the forward-auth check pointed at Authentik's outpost -- the exact directive differs by Caddy/Traefik/NPM; check Authentik's own reverse-proxy integration docs for your specific proxy.

Verify it works

  • Logging into the OIDC-native app (Step 4) redirects to Authentik, prompts for MFA, and returns you logged into the app with no separate app-level password prompt
  • Visiting the forward-auth-protected app (Step 5) while logged out redirects to Authentik's login page before the app itself loads at all
  • After logging in once through Authentik, visiting a second connected app doesn't prompt for login again within the same browser session -- confirms real single sign-on, not just centralized-but-separate logins
  • Failure test: log out of Authentik entirely, then try to access the forward-auth-protected app directly. Confirm it's genuinely blocked, not just showing a login prompt that could be bypassed by hitting the app's own address directly (check this specifically -- a forward-auth misconfiguration can leave the backend reachable directly if the reverse proxy rule isn't actually enforced)

Secure it

  • This is now your single point of failure for every connected app's login: losing access to Authentik (lost MFA device, forgotten admin password, the service itself down) can lock you out of everything behind it -- see Undo/Go further for a recovery plan before you need one.
  • What's exposed: Authentik's own admin UI should be reachable only by you -- consider restricting it further (a separate internal-only URL, or IP allowlisting) beyond just being behind the reverse proxy, since it's the credential store for everything else.
  • Secrets: AUTHENTIK_SECRET_KEY and the Postgres password are real secrets -- generate them properly (not left as placeholder text) and keep them out of version control, same as any other credential in this section.
  • MFA is not optional for the admin account: re-stating Step 3 here deliberately -- it's the single highest-value security control in this entire guide.

Back it up and maintain it

What matters: the authentik_postgres volume holds every user account, application config, and provider setup -- this is now more critical than most individual services it protects, since losing it means reconfiguring every connected app's SSO from scratch. Back it up with the same rigor as the 3-2-1 backups guide elsewhere in this section.

Update cadence: Authentik ships frequent releases; read release notes before updating given how central this service is -- a bad update here affects login for everything behind it, not just one app.

What to monitor: Authentik's own event log (Events in its UI) for failed login attempts, especially against the admin account.

Troubleshooting

Logs: docker logs authentik-server, docker logs authentik-worker.

Symptom Likely cause Diagnostic Fix
OIDC app redirects to Authentik but login fails to complete Redirect URI mismatch between the app's config and the provider config in Authentik Compare the exact redirect URI in both places -- these must match precisely Fix the mismatched URI
Forward-auth app is reachable directly, bypassing Authentik Reverse proxy's forward-auth directive isn't actually applied to that domain/route, or the backend is separately exposed Test the app's direct address/port without going through the configured proxy route Fix the reverse proxy config; ensure the app itself isn't separately port-forwarded
Locked out of Authentik itself (lost MFA device) No recovery method configured before this happened Authentik's documented account-recovery process (varies by version) -- may require direct database access as a last resort Configure a recovery flow or backup admin account before this happens, not after
authentik-worker container keeps restarting Can't reach Postgres/Redis, often a startup-ordering or credential mismatch docker logs authentik-worker for the specific connection error Fix Postgres/Redis connectivity or credentials to match the server container's config exactly
Session doesn't persist between connected apps (re-prompts for login) Cookie domain scoping issue -- Authentik's session cookie needs to be valid across the domains of connected apps Check Authentik's configured cookie domain against your actual app domains Adjust Authentik's cookie domain setting to a common parent domain across your apps

Undo

docker compose down removes the containers; the authentik_postgres volume holds all configuration and must be separately removed to fully undo. Before removing Authentik from any app, reconfigure that app with its own local login again first -- removing Authentik out from under a forward-auth-protected app with no other auth configured can leave it either fully open or fully inaccessible, neither of which is what you want.

Go further

  • Configure a break-glass recovery path (a documented, secured process for regaining access if you're ever locked out) before you need it, not after
  • Extend forward-auth protection to more of the section's guides -- Grafana, Portainer, and others in this content set all support being placed behind it
  • Explore Authentik's group-based policies if multiple people (not just you) need different levels of access to different self-hosted services

Resources

Official documentation:

Source and releases:

Community:

Related DaemonPress projects:


Last verified: 2026-09-21, checked against official Authentik documentation.