Skip to content
Go back

Authentik vs Authelia: SSO for Your Self-Hosted Stack

Updated:
By SumGuy 16 min read
Authentik vs Authelia: SSO for Your Self-Hosted Stack

You’ve got Gitea running. Then Grafana. Then Nextcloud. Then Wiki.js. Then Mealie. Then Immich. Then Uptime Kuma. Then Portainer. Then Jellyfin. Then…

Stop. You’ve got 15 services now, 15 different passwords, and it’s Saturday morning. You wanted to glance at a dashboard before coffee, and instead you’re fumbling with a username field for the fourth time. You forgot which password was which. You’re using the same one everywhere (don’t do that). It’s 2 AM and you’re resetting credentials over SSH instead of having a login page that actually works.

This is what Single Sign-On (SSO) solves. Log in once, access everything. It’s how enterprises have worked for decades. It’s how your Google account works across every Google product. And with one Saturday afternoon, it’s how your home lab can work too — no corporate IT department, no CISM certification, no second mortgage required.

In the self-hosted world, there are two ways to do it: Authelia and Authentik. One is a bouncer at the door. The other is the entire security checkpoint. Picking right depends on what you actually need versus what sounds cool at 2 AM when you’re reading docs instead of sleeping.

Full example: Working Compose files for both setups live at github.com/KingPin/sumguy-examples/tree/main/security/authentik-vs-authelia.

Why Bother With SSO in a Home Lab?

Fair question. You’re the only user. Who are you protecting it from — yourself?

Kind of, yeah. But also:

More practically: a lot of self-hosted apps have mediocre built-in authentication. Some have none at all. Putting an auth layer in front of them means you’re not trusting a random open-source project’s login form to protect your network.

The Problem: 15 Doors, 15 Keys

When you self-host a lot of stuff, each service either:

  1. Has its own user management (and you create duplicates everywhere)
  2. Doesn’t have user management at all (open to the world, hope nobody bad finds it)
  3. Trusts a header from your reverse proxy (requires the proxy to vouch for you)
  4. Speaks OIDC/SAML natively (modern apps — Gitea, Grafana, Nextcloud)

Options 3 and 4 are where SSO lives. Your reverse proxy (or identity provider) sits in front of everything. Before letting a request through, it says “Who are you?” You prove it once with a password (or 2FA), then your services trust the answer and let you in.

The difference between Authelia and Authentik is how much machinery you want behind that vouching system.

Authelia: The Lightweight Bouncer

Authelia is forward auth. It’s a small Go service that sits next to your reverse proxy and handles login. That’s it. That’s the whole job.

Your flow looks like this:

  1. You hit grafana.home.internal
  2. Traefik sees the request, asks Authelia: “Is this person logged in?”
  3. Authelia checks a session cookie. No cookie? Redirect to auth.home.internal/login
  4. You type username/password. Authelia verifies against local user database (or LDAP)
  5. Authelia prompts for 2FA if your policy requires it
  6. Authelia sets a session cookie, redirects you back to Grafana
  7. Traefik checks again: “Yep, they’re good”
  8. Traefik forwards the request to Grafana with headers like Remote-User: kingpin
  9. Grafana sees that header and logs you in as kingpin

Authelia doesn’t manage users through a fancy UI. You write usernames, password hashes, and group memberships in a YAML file. Or you point it at LDAP and let Active Directory (or FreeIPA) manage people.

What Authelia does well:

Authelia + Traefik: The Compose Setup

docker-compose.yml
services:
authelia:
image: authelia/authelia:latest
container_name: authelia
restart: unless-stopped
ports:
- "9091:9091"
environment:
AUTHELIA_JWT_SECRET: ${AUTHELIA_JWT_SECRET}
AUTHELIA_SESSION_SECRET: ${AUTHELIA_SESSION_SECRET}
AUTHELIA_STORAGE_ENCRYPTION_KEY: ${AUTHELIA_STORAGE_ENCRYPTION_KEY}
volumes:
- ./authelia-config.yml:/config/configuration.yml:ro
- ./users.yml:/config/users.yml:ro
- authelia-data:/var/lib/authelia
networks:
- traefik
traefik:
image: traefik:latest
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik-config.yml:/traefik.yml:ro
networks:
- traefik
grafana:
image: grafana/grafana:latest
environment:
GF_AUTH_PROXY_ENABLED: "true"
GF_AUTH_PROXY_HEADER_NAME: "Remote-User"
GF_AUTH_PROXY_HEADER_PROPERTY: "username"
GF_AUTH_PROXY_AUTO_SIGN_UP: "true"
labels:
- "traefik.http.routers.grafana.rule=Host(`grafana.home.internal`)"
- "traefik.http.routers.grafana.middlewares=authelia@docker"
- "traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.home.internal"
- "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email"
networks:
- traefik
volumes:
authelia-data:
networks:
traefik:
driver: bridge

The magic is that forwardauth middleware. Traefik checks with Authelia on every request. No login? Redirect. Logged in? Pass through with Remote-User, Remote-Groups, Remote-Name, and Remote-Email headers attached.

Authelia Config (YAML)

authelia-config.yml
jwt_secret: ${AUTHELIA_JWT_SECRET}
session:
secret: ${AUTHELIA_SESSION_SECRET}
cookies:
- name: authelia_session
domain: home.internal
authelia_url: https://auth.home.internal
inactivity: 1h
expiration: 24h
storage:
encryption_key: ${AUTHELIA_STORAGE_ENCRYPTION_KEY}
local:
path: /var/lib/authelia/db.sqlite3
authentication_backend:
file:
path: /config/users.yml
access_control:
default_policy: deny
rules:
- domain: "*.home.internal"
policy: one_factor
subject:
- "group:users"
- domain: "vault.home.internal"
policy: two_factor
subject:
- "group:users"
totp:
period: 30
skew: 1
webauthn:
display_name: SumGuy Homelab
timeout: 60s
usernameless: false
notifier:
filesystem:
filename: /config/notification.txt

default_policy: deny means nothing is allowed unless an explicit rule says so. Safer than the inverse. Rules are evaluated top-down — the first match wins, so put your stricter two_factor rules above the looser one_factor catch-all.

For 2FA, Authelia supports TOTP, WebAuthn, and Duo. You enable it per user in the users YAML, or per domain in the policy above. Clean. Simple. No extra UI.

The Trade-Off: Authelia’s Limits

Authelia doesn’t have:

If you’re the only person on your homelab, or you’ve got a static team and LDAP handles it, Authelia is perfect. It weighs nothing, starts instantly, and you’re running one extra container.

If you need users to sign up themselves, or you want to let external apps use your homelab as a login provider, you need something bigger.

Authentik: The Full Identity Provider

Authentik is a different beast. Where Authelia is a bouncer, Authentik is the HR department, security desk, and visitor management system rolled into one.

It’s a full Identity Provider (IdP) — other applications delegate their entire authentication to Authentik using industry protocols like OIDC, SAML, and LDAP. Instead of proxying requests via forward auth, apps talk to Authentik directly and trust its answer.

The typical flow with Authentik looks like:

  1. App (Gitea) is configured with Authentik as an OIDC provider
  2. User hits Gitea, clicks “Sign in with Authentik”
  3. Gets redirected to Authentik’s login page
  4. Authentik handles all the auth (password, 2FA, enrollment, whatever your policy says)
  5. Redirects back to Gitea with an authorization code
  6. Gitea exchanges the code for a token, logs the user in

What Authentik does well:

What it costs you:

Authentik Docker Compose

docker-compose.yml
services:
postgresql:
image: postgres:16-alpine
container_name: authentik-db
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${PG_PASS}
POSTGRES_USER: authentik
POSTGRES_DB: authentik
volumes:
- ./database:/var/lib/postgresql/data
networks:
- authentik
redis:
image: redis:alpine
container_name: authentik-redis
restart: unless-stopped
command: --save 60 1 --loglevel warning
volumes:
- ./redis:/data
networks:
- authentik
server:
image: ghcr.io/goauthentik/server:2024.2
container_name: authentik-server
restart: unless-stopped
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
ports:
- "9000:9000"
- "9443:9443"
depends_on:
- postgresql
- redis
networks:
- authentik
- proxy
worker:
image: ghcr.io/goauthentik/server:2024.2
container_name: authentik-worker
restart: unless-stopped
command: worker
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
depends_on:
- postgresql
- redis
networks:
- authentik
networks:
authentik:
internal: true
proxy:
external: true

After it’s up, hit https://your-domain.com/if/flow/initial-setup/ to create your admin account. The UI is genuinely pleasant once you find your bearings.

Authentik’s User Management

Unlike Authelia, Authentik has a web UI at https://authentik.home.internal/if/admin/. You can:

Wiring Up Gitea via OIDC

In Authentik, you create a Provider (OIDC, with Gitea’s callback URL) and an Application (Gitea, pointing at that provider). Then in Gitea’s admin panel:

Authentik auto-creates Gitea users on first login. Done. Same pattern works for Grafana, Nextcloud, Portainer, Argo CD, Vaultwarden’s admin panel, basically anything modern.

Authentik for Forward Auth (Outposts)

For services that don’t speak OIDC (looking at you, half the home-lab dashboard ecosystem), Authentik can still protect them. You deploy a small outpost container, configure it in the Authentik UI, and point your Traefik forwardAuth middleware at the outpost instead of Authelia. Same end result; more moving parts; centralized in the web UI.

Head-to-Head Comparison

FeatureAutheliaAuthentik
Resource use~50-100 MB RAM~500 MB – 1 GB RAM
Setup complexityLow-MediumMedium-High
Web admin UINo (YAML / LDAP)Yes (full admin UI)
Forward AuthYes (primary use case)Yes (via outpost)
OIDC providerLimited (basic only)Full
SAML providerNoYes
LDAP serverNo (client only)Yes
2FA optionsTOTP, WebAuthn, DuoTOTP, WebAuthn, SMS, Duo, more
DatabaseSQLite (default)PostgreSQL + Redis (required)
User self-signupNoYes (configurable flows)
Social loginNoYes (GitHub, Google, Discord, etc.)
User managementYAML files or LDAPWeb UI + LDAP
Written inGoPython
Plays well with TraefikExcellent (drop-in)Good (via outpost)
Learning curveLowMedium

Common Gotchas (Save Yourself the 2 AM Debug)

Neither tool is hard to set up. Both have one or two ways to mess up that will eat your evening if you don’t know to look for them.

Cookie domain mismatch (Authelia). The session.cookies.domain in your Authelia config has to be the parent of every protected service. If your services live at grafana.home.internal and git.home.internal, the cookie domain must be home.internal — not auth.home.internal. Wrong domain means the cookie won’t be sent on requests to other subdomains, and Authelia will think you’re logged out every time you cross a service boundary. Classic source of “wait, why does it keep asking me to log in?”

HTTPS is non-negotiable. Secure cookies require HTTPS. Forward auth without TLS works just well enough to fool you into thinking everything’s fine in dev, then collapses in confusing ways in prod. Run Let’s Encrypt (via Traefik or Caddy) from day one. See Caddy vs Traefik if you’re picking a proxy.

Authentik outpost not connecting. When you create a forward-auth outpost in the UI, Authentik gives you a Compose snippet or a Kubernetes manifest. Use the one it gives you — the embedded outpost token has to match. If you copy a generic outpost config and paste your own token, the outpost will start but never appear as “healthy” in the Authentik UI.

Order of access_control rules. Authelia matches rules top-down. Put two_factor rules above broader one_factor ones, or your stricter policies will never fire. Use default_policy: deny so you fail closed, not open.

Forgetting to back up Authentik’s database. Authelia’s state lives in a SQLite file you can tar and forget. Authentik’s lives in PostgreSQL, and losing it means every WebAuthn key, every TOTP secret, every user config — gone. Use restic or kopia to back up the postgres volume on a schedule.

Time skew on TOTP. If your homelab clock drifts, TOTP codes start failing intermittently. Run chrony or systemd-timesyncd. The error message just says “invalid code” — it does not tell you your clock is 47 seconds off.

Mixing OIDC and forward auth on the same app. If Gitea already does OIDC against Authentik, don’t also put it behind a forward-auth middleware. You’ll bounce between two login flows, neither of which knows about the other. Pick one path per app.

Which Apps Speak Native OIDC?

A non-exhaustive list of home-lab favorites and whether you’ll need forward auth or can use real OIDC. If “Native OIDC” is Yes, prefer Authentik (or any IdP — Keycloak, Zitadel, etc.). If No, both Authelia and Authentik (via outpost) cover it.

AppNative OIDCNotes
Gitea / ForgejoYesOAuth2 + OIDC built-in
GrafanaYesGeneric OAuth + header proxy modes
NextcloudYes (via app)user_oidc app
PortainerYesBusiness edition; Community has OAuth
VaultwardenPartialAdmin panel only; user vault doesn’t do OIDC
JellyfinPluginjellyfin-plugin-sso adds it
ImmichYesFirst-class OAuth2
Home AssistantYes (via add-on)auth_oidc custom component
Paperless-ngxYesVia mozilla-django-oidc
OutlineYesFirst-class OIDC
AdGuard HomeNoHeader auth only
Uptime KumaNoHeader auth only
Pi-holeNoHeader auth only

If your stack is mostly “Yes” rows, Authentik earns its RAM. If it’s mostly “No” rows, save the gigabyte and run Authelia.

Migration Path: Authelia → Authentik

Plenty of people start with Authelia, outgrow it, and want to switch. You don’t have to flip everything at once. Run them in parallel during the cutover:

  1. Stand up Authentik on a different subdomain (authentik.home.internal) alongside the existing Authelia (auth.home.internal). They don’t conflict.
  2. Import users. Authentik has a CSV import, or you can script it via its API. WebAuthn keys and TOTP secrets do not migrate — users re-enroll on first login. Warn them.
  3. Convert OIDC-capable apps first. Gitea, Grafana, Nextcloud — point them at Authentik directly. Test, confirm logins work, leave Authelia protecting the rest.
  4. Move forward-auth services to Authentik outposts one at a time. Change one Traefik middleware label, restart, test.
  5. Tear down Authelia when nothing references it anymore.

The hidden cost is the re-enrollment friction for 2FA. If you have non-technical users on the lab, plan a one-evening “we’re all re-enrolling” session, ideally with a backup plain-password login path you disable afterward.

Honorable Mention: Tinyauth

If Authelia feels too heavy and you literally just want “any login at all in front of these apps,” check out Tinyauth. It’s a single-binary forward-auth daemon with basic 2FA and OAuth login (GitHub, Google) and exactly zero of Authentik’s identity-provider machinery. ~10 MB RAM, one container, config in a single file. Not the right tool if you have more than one user or want fine-grained policies, but worth knowing exists.

Which One Should You Use?

The honest answer: start with Authelia, graduate to Authentik when you outgrow it.

If you’re new to self-hosted SSO, Authelia will get you protected services with 2FA in an afternoon. The YAML config is approachable, the Traefik integration is clean, and you’re not running three extra containers just to get a login page. When you start wanting native OIDC on Gitea and Grafana, or a web UI to manage users, or you start onboarding other people — that’s when Authentik earns its extra RAM.

Pick Authelia if:

Pick Authentik if:

Run both if you’re the kind of person who deploys things just to learn them — which, given that you’ve read this far into a homelab authentication guide, is almost certainly you. Authentik as your IdP for OIDC-capable apps, Authelia (or an Authentik outpost) for the stragglers.

The Bottom Line

Stop logging into twelve services with twelve passwords like some kind of digital cave person. You’ve already gone to the trouble of self-hosting everything — spend one Saturday afternoon putting a real auth layer in front of it.

Authelia: light, fast, file-based, perfect for Traefik forward auth. Authentik: full IdP, web UI, OIDC/SAML, more powerful and more complex. Both are worth knowing. Both will make your home lab meaningfully better.

Your 2 AM self will thank you. Now go drink that coffee before it gets cold.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
Self-Hosted Email in 2026: Mailcow vs Mailu vs Stalwart
Next Post
Ceph for a 3-Node Home Lab: When It Actually Pays Off

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts