What Authelia Gives You
Authelia is a single-binary auth portal that sits between your reverse proxy and your apps. It handles login, password storage (or LDAP), MFA (TOTP, WebAuthn, Duo), and emits an authentication cookie the proxy validates on every subsequent request. For a homelab, it is the sweet spot between "basic auth in nginx" and "full Keycloak/OIDC stack."
Architecture in One Diagram
browser ──► NPM ──(forward-auth)──► Authelia
│
▼
authenticate / require 2FA
│
◄──── 200 OK + cookie (or 401/302) ────
│
NPM proxies to app
Every request to a protected host hits NPM. NPM does a side request to Authelia's /api/verify endpoint. If the cookie is valid and policy allows, the request passes through. Otherwise the user is redirected to the Authelia portal.
Deploy Authelia + Redis with Compose
services:
authelia:
image: authelia/authelia:4.38
container_name: authelia
restart: unless-stopped
volumes:
- ./authelia:/config
environment:
TZ: America/Los_Angeles
networks:
- edge
depends_on:
- redis
expose:
- "9091"
redis:
image: redis:7-alpine
container_name: authelia-redis
restart: unless-stopped
volumes:
- redis_data:/data
networks:
- edge
volumes:
redis_data:
networks:
edge:
external: true
Redis stores session data so users do not get logged out every time you restart Authelia. The edge network is shared with NPM (see the Docker Compose guide).
Minimal configuration.yml
Drop this in ./authelia/configuration.yml:
server:
address: 'tcp://0.0.0.0:9091'
log:
level: info
identity_validation:
reset_password:
jwt_secret: <generate-with-openssl-rand-hex-32>
authentication_backend:
file:
path: /config/users_database.yml
password:
algorithm: argon2
access_control:
default_policy: deny
rules:
- domain: 'auth.example.com'
policy: bypass
- domain: 'public.example.com'
policy: bypass
- domain: '*.lab.example.com'
policy: two_factor
session:
secret: <generate-with-openssl-rand-hex-32>
cookies:
- domain: 'example.com'
authelia_url: 'https://auth.example.com'
default_redirection_url: 'https://lab.example.com'
redis:
host: redis
port: 6379
storage:
encryption_key: <generate-with-openssl-rand-hex-32>
local:
path: /config/db.sqlite3
notifier:
filesystem:
filename: /config/notification.txt
Generate each secret with openssl rand -hex 32. The filesystem notifier writes "emails" (password resets, 2FA enrollment links) to a local file during setup — replace with SMTP before going to production.
Create a User
Hash a password with the Authelia CLI:
docker run --rm authelia/authelia:4.38 \
authelia crypto hash generate argon2 --password 'your-strong-password'
Put it in ./authelia/users_database.yml:
users:
michael:
disabled: false
displayname: 'Michael'
password: '$argon2id$v=19$m=65536,t=3,p=4$...'
email: '[email protected]'
groups:
- admins
Restart Authelia and verify the portal loads at https://auth.example.com.
Wire NPM Forward-Auth
For each protected proxy host in NPM, add a custom location or use the Advanced tab's custom nginx config:
# In the proxy host advanced config:
location /authelia {
internal;
proxy_pass http://authelia:9091/api/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-Method $request_method;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-For $remote_addr;
}
location / {
auth_request /authelia;
auth_request_set $target_url $scheme://$http_host$request_uri;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
error_page 401 =302 https://auth.example.com/?rd=$target_url;
proxy_pass http://upstream-app:8080;
}
Save it once as an NPM snippet/include and reference it from every protected host so you do not copy-paste twenty times. NPM's UI lets you drop these in the Advanced → Custom Nginx Configuration field.
MFA Enrollment
First time a user logs in, Authelia (with two_factor policy) prompts for TOTP enrollment. The QR code link is sent via the notifier — in our filesystem setup that means tail -f ./authelia/notification.txt. Pair with Aegis, Bitwarden, or 1Password.
Strongly recommend WebAuthn (security keys, platform authenticators) as the primary second factor. Enable in config under webauthn:. TOTP as backup.
LDAP Backend (When File Auth Stops Scaling)
Once you have more than a handful of users, swap the file backend for LDAP (LLDAP is a lightweight option):
authentication_backend:
ldap:
implementation: custom
url: ldap://lldap:3890
base_dn: dc=example,dc=com
username_attribute: uid
users_filter: '(&({username_attribute}={input})(objectClass=person))'
groups_filter: '(member={dn})'
user: uid=admin,ou=people,dc=example,dc=com
password: '<bind-password>'
LLDAP gives you a clean UI for managing users/groups and runs in ~30MB of RAM. Pair with Authelia for the auth UX, LDAP for the directory.
Access Control Patterns
- Per-app policy: Tighten
two_factorto specific domains, leave less-sensitive ones atone_factor. - Group-based: Restrict admin UIs to a specific group:
subject: 'group:admins'. - Network-based bypass: Skip MFA when coming from the lab subnet — useful for kiosks. Add
networks:at the rule level.
Operational Notes
- Backup
./authelia/: users, sessions DB, secrets. Without it, restoring means re-enrolling every user's 2FA. - Cookie domain matters: The session cookie applies to
example.comand all subdomains. Subdomains across different parent domains need separate cookie configs. - Auth portal must be on the same parent domain as protected apps (cookie scope). Most homelabs put Authelia at
auth.example.comand apps at*.example.com. - Watch for clock drift: TOTP fails silently if the Authelia container clock drifts. Use NTP on the host.
Common Pitfalls
- Mixed-content redirect loops: Set
X-Forwarded-Protoin NPM. Authelia builds redirect URLs from these headers. - Apps that do their own auth: Either strip the app's login (some support trusted-header SSO via
Remote-User) or accept double login. - API access: Forward-auth breaks token-based API calls. Bypass Authelia for
/api/*on apps that authenticate via tokens, or use Authelia's session API. - Cloudflare Tunnel + Authelia: Both work, but pick one to be the auth gate. Stacking forces two logins. See the Cloudflare Tunnel guide for the alternative.
Validation Checklist
curl -I https://auth.example.comserves the Authelia portal- Protected host redirects to portal on first visit, sets cookie after login
- Second visit to a protected host with cookie passes through without re-auth
- TOTP/WebAuthn enrollment completed for at least one user
- SMTP notifier configured (filesystem only acceptable during setup)
./authelia/is in your backup job (see the Backup Strategies guide)