Key Features
- Single Sign-On across all self-hosted apps
- MFA via TOTP, WebAuthn/FIDO2, Duo Push
- Fine-grained access control rules
- OpenID Connect 1.0 provider (Certifiedโข)
- Brute-force protection & account lockout
- Lightweight single-container deployment
Prerequisites
Before starting, ensure you have the following:
Server Requirements
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2 vCPU |
| RAM | 1 GB | 2 GB |
| Storage | 10 GB SSD | 20 GB SSD |
| OS | Ubuntu 22.04 | Ubuntu 24.04 |
Software Requirements
- โข Docker Engine 24.0+ & Docker Compose V2
- โข Registered domain with DNS pointed to VPS
- โข SSH access with sudo privileges
- โข Basic Linux & YAML familiarity
๐ก RamNode's VPS Standard or Premium plans with 2 GB RAM are ideal for running Authelia alongside Traefik.
Initial Server Setup
Update the System
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl git ca-certificates gnupg lsb-releaseInstall Docker and Docker Compose
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp dockerVerify Installation
docker --version
docker compose versionConfigure Firewall
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableDNS Configuration
Configure DNS A records pointing to your RamNode VPS public IP address:
| Record Type | Host | Purpose |
|---|---|---|
| A | auth.example.com | Authelia portal |
| A | traefik.example.com | Traefik dashboard |
| A | app.example.com | Protected app |
โน๏ธ Replace example.com with your actual domain throughout this guide. DNS propagation may take up to 24 hours.
Create the Project Structure
Create Directory Layout
mkdir -p ~/authelia-stack/authelia/{config,secrets}
mkdir -p ~/authelia-stack/traefik/{config,data,logs}
touch ~/authelia-stack/traefik/data/acme.json
chmod 600 ~/authelia-stack/traefik/data/acme.json
cd ~/authelia-stackExpected Directory Tree
authelia-stack/
โโโ compose.yml
โโโ authelia/
โ โโโ config/
โ โ โโโ configuration.yml
โ โ โโโ users.yml
โ โโโ secrets/
โโโ traefik/
โโโ config/
โ โโโ traefik.yml
โโโ data/
โ โโโ acme.json
โโโ logs/Generate Secrets
Authelia requires several secret values for secure operation. Generate them using the built-in crypto utility:
docker run --rm authelia/authelia:latest authelia \
crypto rand --length 64 --charset alphanumeric \
> ~/authelia-stack/authelia/secrets/jwt_secret
docker run --rm authelia/authelia:latest authelia \
crypto rand --length 64 --charset alphanumeric \
> ~/authelia-stack/authelia/secrets/session_secret
docker run --rm authelia/authelia:latest authelia \
crypto rand --length 64 --charset alphanumeric \
> ~/authelia-stack/authelia/secrets/storage_encryption_keySet Permissions
chmod 600 ~/authelia-stack/authelia/secrets/*โ ๏ธ Warning: Never commit secret files to version control. Add the secrets/ directory to your .gitignore if using Git.
Configure Traefik
Static Traefik Configuration
Create traefik/config/traefik.yml:
api:
dashboard: true
entryPoints:
http:
address: ':80'
http:
redirections:
entryPoint:
to: https
scheme: https
https:
address: ':443'
certificatesResolvers:
letsencrypt:
acme:
email: your-email@example.com
storage: /data/acme.json
httpChallenge:
entryPoint: http
providers:
docker:
exposedByDefault: false
network: proxy
log:
level: INFO
filePath: /logs/traefik.logโน๏ธ Replace your-email@example.com with your actual email for Let's Encrypt certificate notifications.
Configure Authelia
Main Configuration
Create authelia/config/configuration.yml:
---
theme: 'auto'
server:
address: 'tcp://:9091'
log:
level: 'info'
totp:
issuer: 'auth.example.com'
period: 30
skew: 1
identity_validation:
reset_password:
jwt_secret: 'REPLACE_WITH_JWT_SECRET'
authentication_backend:
file:
path: '/config/users.yml'
password:
algorithm: 'argon2'
argon2:
variant: 'argon2id'
iterations: 3
memory: 65536
parallelism: 4
key_length: 32
salt_length: 16
access_control:
default_policy: 'deny'
rules:
- domain: 'public.example.com'
policy: 'bypass'
- domain: 'traefik.example.com'
policy: 'one_factor'
- domain: '*.example.com'
policy: 'two_factor'
session:
secret: 'REPLACE_WITH_SESSION_SECRET'
cookies:
- name: 'authelia_session'
domain: 'example.com'
authelia_url: 'https://auth.example.com'
expiration: '1 hour'
inactivity: '5 minutes'
default_redirection_url: 'https://app.example.com'
regulation:
max_retries: 3
find_time: '2 minutes'
ban_time: '5 minutes'
storage:
encryption_key: 'REPLACE_WITH_ENCRYPTION_KEY'
local:
path: '/data/db.sqlite3'
notifier:
filesystem:
filename: '/data/notification.txt'โน๏ธ Replace all REPLACE_WITH_* values with the contents of the secret files generated in Step 5. For production, replace the filesystem notifier with SMTP.
User Database
First, generate a password hash for your user:
docker run --rm authelia/authelia:latest authelia \
crypto hash generate argon2 --password 'YourSecurePassword'Create authelia/config/users.yml with the generated hash:
---
users:
admin:
displayname: 'Admin User'
password: '$argon2id$v=19$m=65536,t=3,p=4$...'
email: 'admin@example.com'
groups:
- 'admins'
- 'dev'โ ๏ธ Warning: Replace the password hash with the actual output from the hash generation command. Never store plaintext passwords.
Docker Compose Configuration
Create compose.yml in the project root:
---
services:
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/config/traefik.yml:/traefik.yml:ro
- ./traefik/data:/data
- ./traefik/logs:/logs
networks:
- proxy
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.traefik.rule=Host(`traefik.example.com`)'
- 'traefik.http.routers.traefik.entrypoints=https'
- 'traefik.http.routers.traefik.tls.certresolver=letsencrypt'
- 'traefik.http.routers.traefik.service=api@internal'
- 'traefik.http.routers.traefik.middlewares=authelia@docker'
authelia:
image: docker.io/authelia/authelia:latest
container_name: authelia
restart: unless-stopped
volumes:
- ./authelia/config:/config:ro
- ./authelia/data:/data
networks:
- proxy
environment:
TZ: 'America/Chicago'
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.authelia.rule=Host(`auth.example.com`)'
- 'traefik.http.routers.authelia.entrypoints=https'
- 'traefik.http.routers.authelia.tls.certresolver=letsencrypt'
- 'traefik.http.middlewares.authelia.forwardAuth.address=http://authelia:9091/api/authz/forward-auth'
- 'traefik.http.middlewares.authelia.forwardAuth.trustForwardHeader=true'
- 'traefik.http.middlewares.authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Email,Remote-Name'
# Example protected service
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stopped
networks:
- proxy
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.whoami.rule=Host(`app.example.com`)'
- 'traefik.http.routers.whoami.entrypoints=https'
- 'traefik.http.routers.whoami.tls.certresolver=letsencrypt'
- 'traefik.http.routers.whoami.middlewares=authelia@docker'
networks:
proxy:
name: proxy
driver: bridgeDeploy the Stack
Create Data Directory & Launch
mkdir -p ~/authelia-stack/authelia/data
cd ~/authelia-stack
docker compose up -dMonitor Logs
docker compose logs -f autheliaโน๏ธ You should see Authelia report it is listening on port 9091. Verify all containers are running with docker compose ps.
Verify the Deployment
Test the Authelia Portal
Navigate to https://auth.example.com in your browser to access the login portal.
Test a Protected Service
- 1.Navigate to https://app.example.com
- 2.You should be redirected to the Authelia login page
- 3.After authentication, you'll be redirected back to the protected service
- 4.The whoami service will display Remote-User and Remote-Groups headers
Verify Access Control Policies
| Domain | Policy | Expected Behavior |
|---|---|---|
| public.example.com | bypass | No authentication required |
| traefik.example.com | one_factor | Username + password only |
| *.example.com | two_factor | Username + password + TOTP/WebAuthn |
Production Hardening
Enable SMTP Notifications
Replace the filesystem notifier with SMTP for production email delivery:
notifier:
smtp:
address: 'submissions://smtp.example.com:465'
username: 'authelia@example.com'
password: 'your-smtp-password'
sender: 'Authelia <noreply@example.com>'Use Redis for Session Storage
For high-availability deployments, add Redis for sessions:
session:
redis:
host: 'redis'
port: 6379Add a Redis service to your compose.yml:
redis:
image: redis:7-alpine
container_name: redis
restart: unless-stopped
volumes:
- redis_data:/data
networks:
- proxyUpgrade to PostgreSQL Storage
For production workloads, migrate from SQLite to PostgreSQL:
storage:
encryption_key: 'REPLACE_WITH_ENCRYPTION_KEY'
postgres:
address: 'tcp://postgres:5432'
database: 'authelia'
username: 'authelia'
password: 'your-db-password'Additional Security Recommendations
- Enable automatic security updates with unattended-upgrades
- Configure fail2ban to protect SSH access
- Use Docker secrets instead of inline passwords
- Set up regular automated backups of the authelia/data directory
- Keep Docker images updated by periodically pulling latest tags
Troubleshooting
Common Issues
| Issue | Solution |
|---|---|
| 502 Bad Gateway | Ensure Authelia container is running. Check Docker network name matches in compose.yml and Traefik config. |
| Certificate errors | Verify DNS records point to your VPS IP. Check acme.json permissions (must be 600). |
| Login loop / redirects | Ensure session cookie domain matches your root domain. Verify authelia_url is accessible. |
| TOTP codes not working | Verify server time is synced with NTP. Authelia checks NTP on startup. |
| Permission denied | Check file ownership and permissions. Authelia must read config and write to data directory. |
Useful Commands
docker compose logs -f autheliadocker run --rm -v $(pwd)/authelia/config:/config \
authelia/authelia:latest authelia config validate \
--config /config/configuration.ymldocker compose restart autheliadocker inspect --format='{{.State.Health.Status}}' autheliadocker run --rm authelia/authelia:latest authelia \
crypto hash generate argon2 --password 'NewPassword'Protecting Additional Services
To protect any new service behind Authelia, add the following Traefik labels to its Docker Compose service definition:
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.myapp.rule=Host(`myapp.example.com`)'
- 'traefik.http.routers.myapp.entrypoints=https'
- 'traefik.http.routers.myapp.tls.certresolver=letsencrypt'
- 'traefik.http.routers.myapp.middlewares=authelia@docker'Then add a corresponding access control rule to your Authelia configuration.yml to define the authentication policy for the new domain.
๐ Next Steps
- Configure OpenID Connect 1.0 for SSO with Gitea, Portainer, and Grafana
- Set up LDAP authentication backend for centralized user management
- Implement WebAuthn/FIDO2 hardware key support for passwordless auth
- Add per-user and per-group access control rules
