Self-Host Authelia: Authentication Middleware 2026
TL;DR
Authelia (Apache 2.0, ~21K GitHub stars, Go) is authentication middleware that sits in front of your reverse proxy and adds login, 2FA, and access control to any self-hosted app. Unlike Authentik (which is a full identity provider), Authelia is focused purely on authentication — it's lighter, faster, and simpler to configure for the common use case of protecting your homelab behind a single sign-on portal. ~50MB RAM vs Authentik's ~500MB.
Key Takeaways
- Authelia: Apache 2.0, ~21K stars, Go — auth middleware for Caddy/Nginx/Traefik
- Minimal resources: ~50MB RAM — runs on the smallest VPS or Raspberry Pi
- 2FA: TOTP (Google Authenticator), WebAuthn (hardware keys), push notifications
- Access policies: Per-domain access rules — require different auth levels per app
- LDAP support: Connect to LDAP/Active Directory for user management
- vs Authentik: Authelia is simpler, lighter, and not an OAuth2 provider
Authelia vs Authentik
| Feature | Authelia | Authentik |
|---|---|---|
| RAM usage | ~50MB | ~500MB |
| Setup complexity | Low | Medium |
| Config style | YAML files | Web UI |
| OAuth2/OIDC Provider | No | Yes |
| SAML Provider | No | Yes |
| Proxy authentication | Yes | Yes (Outposts) |
| User management | YAML/LDAP | Web UI |
| Flow customization | No | Yes |
| Best for | Simple 2FA gateway | Full IdP with app management |
Part 1: Docker Setup
# docker-compose.yml
services:
authelia:
image: authelia/authelia:latest
container_name: authelia
restart: unless-stopped
volumes:
- ./authelia/config:/config
- authelia_data:/data
ports:
- "9091:9091"
environment:
TZ: America/Los_Angeles
depends_on:
- redis
redis:
image: redis:7-alpine
container_name: authelia-redis
restart: unless-stopped
volumes:
- redis_data:/data
command: redis-server --save 60 1
volumes:
authelia_data:
redis_data:
Part 2: Configuration Files
Create the configuration directory and files:
mkdir -p ./authelia/config
Main configuration
# ./authelia/config/configuration.yml
server:
host: 0.0.0.0
port: 9091
log:
level: info
# JWT secret for session tokens:
jwt_secret: "${JWT_SECRET}" # openssl rand -hex 32
# Default redirection URL after login:
default_redirection_url: "https://yourdomain.com"
# Totp configuration:
totp:
issuer: yourdomain.com
# Duo 2FA (optional):
# duo_api:
# hostname: api-123456789.duosecurity.com
# integration_key: XXXX
# secret_key: XXXX
# File-based user database (simple setup):
authentication_backend:
file:
path: /config/users.yml
password:
algorithm: argon2id
iterations: 3
memory: 65536
parallelism: 4
# Access control policies:
access_control:
default_policy: deny
rules:
# Allow public access to specific paths:
- domain: "public.yourdomain.com"
policy: bypass
# Require 1FA for general apps:
- domain: "*.yourdomain.com"
policy: one_factor
# Require 2FA for sensitive apps:
- domain: "vault.yourdomain.com"
policy: two_factor
- domain: "portainer.yourdomain.com"
policy: two_factor
- domain: "proxmox.yourdomain.com"
policy: two_factor
# Session configuration:
session:
name: authelia_session
secret: "${SESSION_SECRET}" # openssl rand -hex 32
expiration: 1h
inactivity: 5m
remember_me_duration: 1M
redis:
host: redis
port: 6379
# Storage (SQLite for simplicity):
storage:
encryption_key: "${STORAGE_ENCRYPTION_KEY}" # openssl rand -hex 32
local:
path: /data/db.sqlite3
# Notification (email):
notifier:
smtp:
username: authelia@yourdomain.com
password: "${MAIL_PASSWORD}"
host: mail.yourdomain.com
port: 587
sender: "Authelia <authelia@yourdomain.com>"
tls:
skip_verify: false
User database
# ./authelia/config/users.yml
# Generate password hash: docker run authelia/authelia:latest authelia crypto hash generate argon2 --password "yourpassword"
users:
yourname:
disabled: false
displayname: "Your Name"
password: "$argon2id$v=19$m=65536,t=3,p=4$..." # hash from above
email: you@yourdomain.com
groups:
- admins
- users
alice:
disabled: false
displayname: "Alice"
password: "$argon2id$v=19$m=65536,t=3,p=4$..."
email: alice@yourdomain.com
groups:
- users
# Generate a password hash:
docker run --rm authelia/authelia:latest \
authelia crypto hash generate argon2 --password "yourpassword"
Part 3: HTTPS with Caddy
Caddy forwards authentication checks to Authelia:
# Authelia itself:
auth.yourdomain.com {
reverse_proxy authelia:9091
}
# Snippet for protected apps:
(authelia) {
forward_auth authelia:9091 {
uri /api/verify?rd=https://auth.yourdomain.com
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
trusted_proxies private_ranges
}
}
# Portainer (requires 2FA per policy):
portainer.yourdomain.com {
import authelia
reverse_proxy portainer:9000
}
# Grafana:
grafana.yourdomain.com {
import authelia
reverse_proxy grafana:3000
}
# Nextcloud (bypass Authelia — Nextcloud has its own auth):
nextcloud.yourdomain.com {
reverse_proxy nextcloud:80
}
Part 4: Nginx Integration
For Nginx users:
# /etc/nginx/snippets/authelia-location.conf
location /authelia {
internal;
proxy_pass http://127.0.0.1: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-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
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-Method $request_method;
proxy_connect_timeout 5s;
proxy_read_timeout 10s;
}
# Protected virtual host:
server {
listen 443 ssl;
server_name portainer.yourdomain.com;
include /etc/nginx/snippets/ssl.conf;
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;
error_page 401 =302 https://auth.yourdomain.com/?rd=$target_url;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
proxy_pass http://portainer:9000;
}
}
Part 5: 2FA Setup
TOTP (Authenticator apps)
- Log in to any protected app → redirected to Authelia
- Click Register device → Time-based One-Time Password
- Scan QR code with Google Authenticator, Aegis, or Bitwarden
- Enter code to confirm
WebAuthn (Hardware keys / Face ID / Touch ID)
- Log in → Register device → WebAuthn device
- Plug in YubiKey or use Face ID/Touch ID on supported devices
- Name the device → confirm
Disable 2FA for specific apps
# In configuration.yml:
access_control:
rules:
# Internal apps — no 2FA needed:
- domain: "homeassistant.yourdomain.com"
networks:
- 192.168.1.0/24 # Local network only
policy: bypass
# Or: one_factor (password only, no TOTP):
- domain: "jellyfin.yourdomain.com"
policy: one_factor
Part 6: LDAP Backend
For larger setups, replace the file backend with LDAP:
# configuration.yml:
authentication_backend:
ldap:
implementation: custom # or: activedirectory, lldap, glauth
url: "ldap://lldap:3890"
base_dn: "dc=yourdomain,dc=com"
username_attribute: uid
additional_users_dn: "ou=people"
users_filter: (&({username_attribute}={input})(objectClass=person))
additional_groups_dn: "ou=groups"
groups_filter: "(member={dn})"
group_name_attribute: cn
mail_attribute: mail
display_name_attribute: displayName
user: "cn=admin,dc=yourdomain,dc=com"
password: "${LDAP_PASSWORD}"
Pair with lldap — a lightweight LDAP server designed specifically for self-hosted setups.
Part 7: Access Control Patterns
access_control:
default_policy: deny
rules:
# 1. Bypass healthcheck endpoints:
- domain: "*.yourdomain.com"
resources:
- "^/health$"
- "^/metrics$"
policy: bypass
# 2. Local network gets 1FA only:
- domain: "*.yourdomain.com"
networks:
- 192.168.1.0/24
- 10.0.0.0/8
policy: one_factor
# 3. Admin group gets access to admin tools:
- domain: "portainer.yourdomain.com"
subject:
- "group:admins"
policy: two_factor
# 4. Users group gets access to media:
- domain: "jellyfin.yourdomain.com"
subject:
- "group:users"
- "group:admins"
policy: one_factor
# 5. Block everyone else from everything:
- domain: "*.yourdomain.com"
policy: deny
Maintenance
# Update:
docker compose pull
docker compose up -d
# Validate configuration:
docker exec authelia authelia validate-config --config /config/configuration.yml
# Backup:
# SQLite database:
docker cp authelia:/data/db.sqlite3 authelia-backup-$(date +%Y%m%d).db
# Config files:
cp -r ./authelia/config ./authelia-config-backup-$(date +%Y%m%d)
# Logs:
docker compose logs -f authelia
# Test authentication flow:
curl -v "https://auth.yourdomain.com/api/health"
See also: Authentik — full identity provider with OAuth2/OIDC for apps that need programmatic authentication
See all open source security tools at OSSAlt.com/categories/security.