Authelia - Easy SSO
Authelia is a simple way to get 2-factor authentication on all your homelab services.
I have Authelia in front of almost all HTTPS endpoints in my homelab via Caddy forward auth.
This means that all requests that are sent through also send a request through to Caddy.
Setup
I set up Authelia in a Debian 13 LXC container.
Pretty simple setup, you can just follow the docs on Authelia's website here.
Add the APT Repository
Add the required packages and download the repository key:
sudo apt install ca-certificates curl gnupg
sudo curl -fsSL https://www.authelia.com/keys/authelia-security.gpg -o /usr/share/keyrings/authelia-security.gpg
Verify the downloaded key:
gpg --no-default-keyring --keyring /usr/share/keyrings/authelia-security.gpg --list-keys --with-subkey-fingerprint
Example output showing the correct Key IDs:
/usr/share/keyrings/authelia-security.gpg
-----------------------------------------
pub rsa4096 2025-06-27 [SC]
192085915BD608A458AC58DCE461FA1531286EEA
uid [ unknown] Authelia Security <[email protected]>
uid [ unknown] Authelia Security <[email protected]>
sub rsa2048 2025-06-27 [E] [expires: 2033-06-25]
7DBA42FED0069D5828A44079975E8FFC6876AFBB
sub rsa2048 2025-06-27 [SA] [expires: 2033-06-25]
C387CC1B5FFC25E55F75F3E6A228F3BD04CC9652
Add the repository to sources.list.d:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/authelia-security.gpg] https://apt.authelia.com stable main" | \
sudo tee /etc/apt/sources.list.d/authelia.list > /dev/null
Update the cache and install:
Configuration
The config for Authelia will depend on your setup. You can get really technical with it.
I run my Authelia with a Postgres Database, Redis, YAML users database.
I could probably upgrade the users database to something else, but it's just me! If I was in a more enterprise environment, this would 100% be LDAP.
---
###############################################################
# Authelia configuration #
###############################################################
theme: dark
server:
endpoints:
authz:
forward-auth:
implementation: 'ForwardAuth'
log:
level: 'debug'
identity_validation:
reset_password:
jwt_secret: 'secret'
authentication_backend:
file:
path: '/etc/authelia/users_database.yml'
watch: false
search:
email: false
case_insensitive: false
password:
algorithm: 'bcrypt'
bcrypt:
variant: 'standard'
cost: 12
access_control:
default_policy: 'deny'
rules:
# Rules applied to everyone
- domain: '*.mydomain.com'
policy: 'two_factor'
- domain: 'mydomain.com'
policy: 'two_factor'
totp:
disable: false
issuer: 'authelia.com'
algorithm: 'sha1'
digits: 6
period: 30
skew: 1
secret_size: 32
allowed_algorithms:
- 'SHA1'
allowed_digits:
- 6
allowed_periods:
- 15
disable_reuse_security_policy: false
session:
secret: 'secret'
redis:
host: '/run/redis/redis-server.sock'
port: 0
timeout: '5s'
max_retries: 0
database_index: 0
maximum_active_connections: 8
minimum_idle_connections: 0
cookies:
- name: 'authelia_session'
domain: 'mydomain.com'
authelia_url: 'https://auth.mydomain.com'
expiration: '1 hour'
inactivity: '5 minutes'
regulation:
max_retries: 3
find_time: '2 minutes'
ban_time: '5 minutes'
storage:
encryption_key: 'secret'
postgres:
address: 'tcp://postgresql-lxc.localdomain:5432'
database: 'secret'
username: 'secret'
password: 'secret'
schema: 'public'
timeout: '5s'
notifier:
disable_startup_check: false
filesystem:
filename: '/etc/authelia/notification.txt'
identity_providers:
oidc:
hmac_secret: 'secret'
jwks:
- key: |
-----BEGIN PRIVATE KEY-----
SECRET
-----END PRIVATE KEY-----
enable_client_debug_messages: false
minimum_parameter_entropy: 8
enforce_pkce: 'public_clients_only'
enable_pkce_plain_challenge: false
enable_jwt_access_token_stateless_introspection: false
discovery_signed_response_alg: 'none'
discovery_signed_response_key_id: ''
require_pushed_authorization_requests: false
authorization_policies:
policy_name:
default_policy: 'deny'
rules:
- policy: 'two_factor'
subject: 'user:username'
cors:
endpoints:
- 'authorization'
- 'token'
- 'revocation'
- 'introspection'
allowed_origins:
- 'https://auth.mydomain.com'
allowed_origins_from_client_redirect_uris: false
clients:
- client_id: 'nextcloud'
client_name: 'NextCloud'
client_secret: 'secret'
public: false
authorization_policy: 'two_factor'
consent_mode: 'implicit'
require_pkce: true
pkce_challenge_method: 'S256'
redirect_uris:
- 'https://nextcloud.mydomain.com/apps/oidc_login/oidc'
scopes:
- 'openid'
- 'profile'
- 'email'
- 'groups'
response_types:
- 'code'
grant_types:
- 'authorization_code'
access_token_signed_response_alg: 'none'
userinfo_signed_response_alg: 'none'
token_endpoint_auth_method: 'client_secret_basic'
- client_id: 'miniflux'
client_name: 'Miniflux'
client_secret: 'secret'
public: false
consent_mode: 'implicit'
authorization_policy: 'two_factor'
redirect_uris:
- 'https://miniflux.mydomain.com/oauth2/oidc/callback'
scopes:
- 'openid'
- 'profile'
- 'email'
userinfo_signed_response_alg: 'none'
token_endpoint_auth_method: 'client_secret_basic'
- client_id: 'paperless'
client_name: 'Paperless'
client_secret: 'secret'
public: false
require_pkce: true
pkce_challenge_method: 'S256'
consent_mode: 'implicit'
authorization_policy: 'two_factor'
redirect_uris:
- 'https://paperless.mydomain.com/accounts/oidc/authelia/login/callback/'
scopes:
- 'openid'
- 'profile'
- 'email'
userinfo_signed_response_alg: 'none'
token_endpoint_auth_method: 'client_secret_basic'
- client_id: 'gitea'
client_name: 'Gitea'
client_secret: 'secret'
public: false
authorization_policy: 'two_factor'
consent_mode: 'implicit'
redirect_uris:
- 'https://gitea.mydomain.com/user/oauth2/authelia/callback'
scopes:
- 'openid'
- 'email'
- 'profile'
response_types:
- 'code'
grant_types:
- 'authorization_code'
access_token_signed_response_alg: 'none'
userinfo_signed_response_alg: 'none'
token_endpoint_auth_method: 'client_secret_basic'
Users Database
---
###############################################################
# Users Database #
###############################################################
# This file can be used if you do not have an LDAP set up.
# List of users
users:
username:
disabled: false
displayname: 'username'
# Password is authelia
password: '$6$rounds=50000$BpLnfgDsc2WD8F2q$Zis.ixdg9s/UOJYrs56b5QEZFiZECu0qZVNsIYxBaNJ7ucIL.nlxVCT5tqh8KHG8X4tlwCFm5r6NTOZZ5qRFN/'
email: '[email protected]'
groups:
- 'admins'
- 'dev'
- 'admin'
...
systemd
Make the systemd unit.
[Unit]
Description=Authelia authentication and authorization server
Documentation=https://www.authelia.com
After=multi-user.target
[Service]
User=authelia
Group=authelia
UMask=027
Environment=AUTHELIA_SERVER_DISABLE_HEALTHCHECK=true
ExecStart=/usr/bin/authelia --config /etc/authelia/configuration.yml
SyslogIdentifier=authelia
CapabilityBoundingSet=
NoNewPrivileges=yes
RestrictNamespaces=yes
ProtectHome=true
PrivateDevices=yes
PrivateUsers=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
[Install]
WantedBy=multi-user.target
Then enable it.
Redis
Redis is super simple to setup and we can just use the defaults here.
Caddy Integration
I use Caddy to integrate Authleia to all my services.
You can that up pretty easily.
@miniflux host miniflux.mydomain.com
handle @miniflux {
forward_auth {env.AUTHELIA_HOST} {
uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
reverse_proxy miniflux-lxc.localdomain:8080 {
header_up Cookie "authelia_session=[^;]+" "authelia_session=_"
}
}
I like to include the blank authelia_session so that the backend servers do not see the session cookie for Authelia.