Caddy bringt deine Apps öffentlich ans Netz, der Mail-Relay schickt Notifications raus. Was fehlt: ein Login davor. Für Apps wie Vaultwarden oder Immich willst du nicht den App-eigenen Login als einzige Hürde haben – du willst eine Schicht davor, die schon vor dem ersten App-Aufruf prüft, ob hier überhaupt jemand reinkommen darf. Und das nicht nur mit einem Passwort, sondern mit 2FA (Zwei-Faktor-Authentifizierung): zusätzlich zum Passwort ein zweiter Faktor, typischerweise ein 6-stelliger Code, der sich alle 30 Sekunden ändert und nur auf deinem Handy lebt. Selbst wenn dein Passwort geleakt wird, kommt niemand ohne dein Handy rein.
Genau das macht Authelia. Eine kleine, ressourcenarme Anwendung, die zwischen Caddy und deinen persönlichen Apps sitzt und entscheidet: gültiger Login mit zweitem Faktor? → weiter zur App. Sonst → Login-Portal. Eine einzige Session deckt alle deine geschützten Apps ab – einmal eingeloggt, kommst du bis zum Cookie-Ablauf überall rein, ohne dich jedes Mal erneut anmelden zu müssen.
Wichtig vorab: Authelia ist der Kontrolleur an der Tür, kein Ersatz für den Login der App selbst. Caddy ist die einzige Eingangstür – aber er prüft nicht, wer reinkommt. Genau das macht Authelia: Du meldest dich an, Authelia entscheidet, ob du weiter darfst. Danach fragen Vaultwarden, Immich oder Paperless trotzdem nochmal nach ihrem eigenen Account. Klingt zunächst nach doppelter Arbeit, ist aber genau das Richtige: Die App-Logins bleiben App-Logins (mit ihren eigenen Rollen, Berechtigungen, Datenbanken), und Authelia stellt davor sicher, dass überhaupt nur angemeldete Nutzer die App zu sehen bekommen.
Am Ende hast du
- Authelia + Redis laufen als UI-Defined Stack in Komodo
- Eine eigene Login-Seite unter
auth.deinedomain.de, geschützt mit HTTPS - Einen ersten Admin-User mit einem starken Passwort
- 2FA eingerichtet: Beim Login musst du zusätzlich einen 6-stelligen Code aus einer Authenticator-App eingeben
- Authelia kann Mails verschicken – z. B. für „Passwort vergessen"-Funktionen
- Einmal eingeloggt bleibst du eingeloggt, auch nach Updates oder Neustarts
- Authelia-Logs werden als Datei mitgeschrieben – die brauchen wir später für CrowdSec
- In Caddy reicht künftig eine Zeile pro App, um sie hinter den Login zu hängen
- Eine durchdachte Schutz-Strategie: Standard-Schutz für alle Apps, höchste Stufe für die wirklich sensiblen
Voraussetzungen
- Artikel 02–08 abgeschlossen
- Caddy läuft (Artikel 07) – inkl. Wildcard-DNS, dadurch ist
auth.deinedomain.deschon vorbereitet - Mail-Relay läuft (Artikel 08) – Authelia verschickt Account-Setup-Mails darüber
- Komodo läuft, Server
vps-homelabist eingebunden - Eine TOTP-App auf dem Smartphone (Empfehlung wie in Artikel 05: 2FAS Auth, Aegis oder Bitwarden Authenticator)
1. Was Authelia für dieses Setup leistet
In Stichpunkten:
- Ein Login für alles – einmal an
auth.deine-domain.deeinloggen, danach gelten die Cookies für alle Subdomains vondeine-domain.de. Vaultwarden, Immich, Paperless – alle bekommen denselben Login-Status. - 2FA per Code aus der Authenticator-App – zweiter Faktor zusätzlich zum Passwort. Pflicht oder optional je nach App.
- Schutz vor automatischen Login-Versuchen – nach mehreren Fehlversuchen wird der Account für eine konfigurierbare Zeit gesperrt. Das bremst Bots aus, die Passwörter durchprobieren.
- Self-Service – Passwort-Reset per E-Mail (über den Mail-Relay), 2FA-Setup im User-Portal.
- Persistente Sessions – durch Redis als Session-Store überleben Logins auch Stack-Updates und Container-Restarts.
Was Authelia nicht macht: den App-Login ersetzen. Wenn du dich bei Authelia eingeloggt hast und Vaultwarden öffnest, fragt Vaultwarden trotzdem nochmal nach seinem eigenen Master-Passwort. Authelia entscheidet nur: Darf diese Person die App überhaupt sehen? Was innerhalb der App passiert, regelt die App selbst.
Für die Apps in Phase 3 (Vaultwarden, Immich, Paperless) ist das genau das richtige Modell. Sie haben jeweils ihren eigenen Login; Authelia liefert die vorgelagerte Schutzschicht.
2. So funktioniert Forward Auth
Wenn du https://vault.deinedomain.de aufrufst, passiert das hier:
1. Browser → Caddy: GET https://vault.deinedomain.de/
2. Caddy → Authelia: GET /api/authz/forward-auth (im internen Netz)
3. Authelia prüft die Cookies
4a. Cookie gültig + 2FA OK: 200 OK + User-Header
4b. Cookie fehlt/ungültig: 301 Redirect zu auth.deinedomain.de
5a. Caddy bei 200 OK: reverse_proxy zur App, User-Header dran
5b. Caddy bei Redirect: Browser landet auf Login-Portal
6. Nach Login: Authelia setzt Cookie + Redirect zurück zur App
Das Schöne: Vaultwarden, Immich oder Paperless wissen nicht, dass Authelia existiert. Sie sehen normale HTTP-Anfragen, ggf. mit Headern wie Remote-User und Remote-Email zur Identifikation. Mehr nicht. Du kannst Authelia jederzeit vor jede beliebige Web-App schalten, solange Caddy davor sitzt.
Caddy spricht Authelia ausschließlich intern über das homelab_net an – http://authelia:9091. Authelia veröffentlicht keine Ports am Host, genau wie Mail-Relay und Komodo-Periphery.
3. Access-Control-Strategie: pragmatisch statt paranoid
Authelia kennt vier Policy-Stufen pro Domain/Pfad:
| Policy | Bedeutung |
|---|---|
deny |
Zugriff komplett blockiert |
bypass |
Authelia greift nicht ein, App-Login allein |
one_factor |
Passwort allein reicht |
two_factor |
Passwort + TOTP/WebAuthn |
Die Strategie für diese Serie
default_policy: one_factor
→ two_factor + group:admins für sensible Apps (Vaultwarden, Immich, Paperless, File Browser)
→ two_factor für persönliche Apps ohne erhöhte Anforderungen
→ bypass für die Auth-Subdomain selbst
Warum one_factor als Default und nicht deny?
Beide funktionieren grundsätzlich, aber one_factor ist als Sicherheitsnetz pragmatischer:
- Wenn du eine neue Subdomain in Caddy einbaust und
import authsetzt, aber vergisst, eineaccess_control-Regel zu ergänzen → mitdefault_policy: denybekommst du 401 statt eines Login-Formulars. Du musst Authelia anfassen, bevor irgendwer reinkommt. - Mit
default_policy: one_factorist die unbekannte Subdomain sofort mit Passwort-Login geschützt. Du kannst in Ruhe entscheiden, ob sietwo_factorbraucht – aber sie hängt nie ungeschützt im Netz. - Wirklich öffentliche Subdomains (Ghost-Blog) haben in Caddy ohnehin kein
import auth– die fragen Authelia nie. Die Default-Policy ist also nur das Fallback für Subdomains, die durch Authelia gehen.
Zweiter Filter: subject: ["group:admins"]
Für die wirklich sensiblen Apps reicht two_factor allein nicht aus. Wir verlangen zusätzlich, dass der eingeloggte User in der Gruppe admins ist:
- domain: 'vault.deine-domain.de'
policy: 'two_factor'
subject: ['group:admins']
Was das bringt: Selbst wenn du später Family-Member oder Freunde als User in users_database.yml hinzufügst (z. B. für Outline oder Immich-Sharing), kommen die ohne admins-Mitgliedschaft nicht auf Vaultwarden oder Paperless – auch nicht mit gültigem Passwort + 2FA.
Bei nur einem User
Wenn du gerade Single-User unterwegs bist, ändert der
group:admins-Filter nichts an deinem Erlebnis – du bist ja inadmins. Aber wenn du das Pattern von Anfang an mitnimmst, brauchst du beim ersten Zweit-User keine Konfigurations-Migration. Kostet jetzt nichts, schützt später konsequent.
Empfohlene Vergabe in dieser Serie
| App | Policy | Begründung |
|---|---|---|
| Vaultwarden (Art 12) | two_factor + group:admins |
Master-Passwörter – nichts Sensibleres |
| Immich (Art 13) | two_factor + group:admins |
Persönliche Fotos, oft auch Standortdaten |
| Paperless-ngx (Art 14) | two_factor + group:admins |
Ausweise, Verträge, Rechnungen |
| File Browser | two_factor + group:admins |
Direkter Zugriff aufs Filesystem – hochsensibel |
| Outline (Art 17) | two_factor |
Auch für Nicht-Admins zugänglich (z. B. geteiltes Wiki) |
| Persönliche Bookmarks / RSS | two_factor |
Standard für persönliche Apps |
bypassvermeidenVersuchung: „Mein Ghost-Blog soll sowieso öffentlich sein – setze ich auf
bypass." Falsch herum gedacht. Wenn eine Domain in Authelia aufbypasssteht, geht jeder Request unverändert durch – also auch/ghost/admin. Den Admin-Bereich schützt man besser gar nicht über Authelia, sondern wie in Artikel 07 angedeutet per Caddy-Regel auf VPN-only.bypassbrauchst du nur für die Authelia-Subdomain selbst.
4. DNS-Vorbereitung
Wenn du Artikel 07 mit Wildcard-DNS abgeschlossen hast, ist hier nichts zu tun. Die Subdomain auth.deinedomain.de zeigt durch den *-A-Record bereits auf deinen VPS.
Quick-Check:
dig +short auth.deinedomain.de
Erwartung: Deine VPS-IPv4 erscheint.
Wenn nicht: Du hast Wildcard möglicherweise nicht gesetzt – zurück zu Artikel 07, Abschnitt 3.
5. Secrets generieren
Setup-Reihenfolge
Wir bereiten zuerst alles auf dem Server vor (Sektionen 5–8: Secrets, Verzeichnisse, Konfigurationsdateien), bevor der Stack in Komodo angelegt wird (Sektionen 9–13). Dadurch ist beim ersten Deploy bereits alles vorhanden, was Authelia zum Starten braucht.
Authelia braucht vier zufällige Geheimnisse:
- JWT Secret – signiert die Tokens für den Identity-Validation-Flow (Passwort-Reset etc.)
- Session Secret – signiert die Session-Cookies, die nach dem Login an alle deine Subdomains gehen
- Storage Encryption Key – verschlüsselt die TOTP-Secrets und andere sensible Daten in der lokalen Datenbank
- Redis Password – schützt Redis intern, auch wenn es nur im internen Netz erreichbar ist (eine Schutzschicht mehr)
Auf dem Server generieren:
echo "JWT Secret: $(openssl rand -hex 32)"
echo "Session Secret: $(openssl rand -hex 32)"
echo "Storage Enc Key: $(openssl rand -hex 32)"
echo "Redis Password: $(openssl rand -base64 24)"
Alle Werte in deinen Passwort-Manager. Sobald sie in der .env stehen, brauchst du sie nur noch im Disaster-Recovery-Fall.
⚠️ Der Storage Encryption Key ist nicht ersetzbar
JWT und Session Secret kannst du im Notfall neu generieren – User müssen sich danach nur neu einloggen. Der Storage Encryption Key dagegen verschlüsselt die TOTP-Secrets in der SQLite-Datenbank. Verlierst du ihn, sind die TOTP-Setups deiner User unbrauchbar – jeder muss seine 2FA-App neu koppeln. Backup dieses Werts ist Pflicht, nicht Empfehlung.
6. Verzeichnisse vorbereiten
Bevor wir die Datei schreiben, einmalig die komplette Verzeichnisstruktur vorbereiten. Komodo legt das Stack-Verzeichnis sonst erst beim Deploy an – aber dann sind wir zu spät dran, weil Authelia ohne Konfigurationsdatei nicht startet:
sudo mkdir -p /srv/docker/stacks/authelia/{config,data,log,redis-data}
sudo chown -R 1000:1000 /srv/docker/stacks/authelia/{data,log}
Hintergrund: Authelia läuft im Container als User 1000. Wenn das Host-Verzeichnis root gehört, scheitert SQLite beim Schreiben in
./dataund der Log-Writer in./log. Dasredis-data-Verzeichnis braucht keinenchown, weil der Redis-Container intern als Userredis(UID 999) läuft und sich seine Rechte selbst setzt.
7. configuration.yml
Das ist die zentrale Konfigurationsdatei von Authelia. Anders als compose.yaml und .env lässt sich diese Datei nicht sinnvoll über die Komodo-UI pflegen – wir legen sie direkt auf dem Server an.
Per VS Codium (falls du Remote-SSH in Artikel 07 eingerichtet hast):
Datei /srv/docker/stacks/authelia/config/configuration.yml neu anlegen und folgenden Inhalt einfügen.
Per SSH:
sudo nano /srv/docker/stacks/authelia/config/configuration.yml
In beiden Fällen ist der Inhalt:
###############################################################
# Authelia - Hauptkonfiguration
###############################################################
theme: 'dark'
# Server
server:
address: 'tcp://0.0.0.0:9091/'
endpoints:
authz:
forward-auth:
implementation: 'ForwardAuth'
# Logs in Datei – vorbereitet für CrowdSec in Artikel 10
log:
level: 'info'
format: 'text'
file_path: '/log/authelia.log'
keep_stdout: true
# TOTP-Setup (Issuer = was in der Authenticator-App angezeigt wird)
totp:
issuer: '{{ env "AUTHELIA_TOTP_ISSUER" }}'
period: 30
skew: 1
# WebAuthn (Passkeys / YubiKey – optional, aber empfehlenswert)
webauthn:
display_name: 'Authelia'
attestation_conveyance_preference: 'indirect'
timeout: '60 seconds'
# Auth-Backend: lokale YAML-Datei (gut für 1–10 User; bei mehr → Authentik-Bonus)
authentication_backend:
file:
path: '/config/users_database.yml'
watch: true
password:
algorithm: 'argon2'
# Access Control – one_factor als Sicherheitsnetz, sensible Apps mit Admin-Filter
access_control:
default_policy: 'one_factor'
rules:
# Auth-Subdomain selbst: muss bypass sein, sonst kein Login möglich
- domain: 'auth.{{ env "AUTHELIA_DOMAIN" }}'
policy: 'bypass'
# Sensible Apps: 2FA + Admin-Mitgliedschaft erforderlich
- domain:
- 'vault.{{ env "AUTHELIA_DOMAIN" }}'
- 'immich.{{ env "AUTHELIA_DOMAIN" }}'
- 'docs.{{ env "AUTHELIA_DOMAIN" }}'
- 'files.{{ env "AUTHELIA_DOMAIN" }}'
policy: 'two_factor'
subject: ['group:admins']
# Persönliche Apps mit 2FA, aber ohne Admin-Filter (z. B. geteiltes Wiki)
- domain: 'wiki.{{ env "AUTHELIA_DOMAIN" }}'
policy: 'two_factor'
# Session – Redis als Store + Cookie auf Parent-Domain für echtes SSO
session:
secret: '{{ env "AUTHELIA_SESSION_SECRET" }}'
redis:
host: 'redis'
port: 6379
password: '{{ env "AUTHELIA_REDIS_PASSWORD" }}'
database_index: 0
cookies:
- name: 'authelia_session'
domain: '{{ env "AUTHELIA_DOMAIN" }}'
authelia_url: '{{ env "AUTHELIA_URL" }}'
default_redirection_url: 'https://{{ env "AUTHELIA_DOMAIN" }}'
expiration: '12 hours'
inactivity: '45 minutes'
remember_me: '1 month'
# Rate-Limiting bei Login-Versuchen
regulation:
max_retries: 3
find_time: '2 minutes'
ban_time: '10 minutes'
# Lokale SQLite-DB für User-Daten, TOTP-Secrets etc.
storage:
encryption_key: '{{ env "AUTHELIA_STORAGE_ENCRYPTION_KEY" }}'
local:
path: '/data/db.sqlite3'
# Notifier – nutzt den Mail-Relay aus Artikel 08
notifier:
disable_startup_check: false
smtp:
address: 'smtp://mail-relay:2500'
sender: '{{ env "AUTHELIA_SMTP_SENDER" }}'
disable_require_tls: true
disable_starttls: true
# Identity Validation – JWT-Secret für Passwort-Reset-Links
identity_validation:
reset_password:
jwt_secret: '{{ env "AUTHELIA_JWT_SECRET" }}'
Was die wichtigsten Blöcke machen
| Block | Was passiert |
|---|---|
server.endpoints.authz.forward-auth |
Aktiviert genau den Endpoint, den Caddys forward_auth gleich abfragt: /api/authz/forward-auth |
log.file_path + keep_stdout |
Logs gehen zusätzlich in /log/authelia.log – CrowdSec liest die in Artikel 10. keep_stdout: true bedeutet: Komodo sieht weiterhin die Logs in der UI |
authentication_backend.file |
Lokale YAML als User-DB. watch: true heißt: Änderungen werden live übernommen, ohne Container-Restart |
access_control.default_policy: one_factor |
Sicherheitsnetz: unbekannte Subdomains bekommen mindestens Passwort-Schutz |
subject: ['group:admins'] |
Zweiter Filter zusätzlich zu 2FA: der User muss auch in der admins-Gruppe sein |
session.redis |
Sessions liegen in Redis statt im Container-Speicher → überleben Restarts |
session.cookies[0].domain |
Parent-Domain. Cookie gilt für alle Subdomains → echtes SSO |
regulation.max_retries: 3 |
Nach 3 Fehlversuchen ist der Account 10 Minuten gesperrt – einfache Brute-Force-Abwehr |
notifier.smtp |
Authelia verschickt Mails über mail-relay:2500 – ohne TLS, ohne Auth, da das Docker-Netz lokal ist (siehe Artikel 08) |
identity_validation.reset_password.jwt_secret |
Signiert die Token in Passwort-Reset-Links per Mail. Wichtig in 4.39+ – ältere Anleitungen zeigen oft noch eine Top-Level-jwt_secret-Variable, die ist abgekündigt |
Warum SQLite und nicht PostgreSQL?
Die offiziellen Authelia-Docs raten beim File-Auth-Backend zu PostgreSQL und nennen SQLite „für Dev und Testing". Der Hintergrund ist Multi-Instance-Skalierung – mehrere Authelia-Container parallel hinter einem Load Balancer.
Im Self-Hosting für 1–3 User auf einem VPS ist das Szenario komplett irrelevant. SQLite läuft hier seit Jahren stabil, braucht null Setup und sichert sich mit dem
./data-Verzeichnis automatisch mit. Wer später echte Skalierung braucht, hat ohnehin schon den Sprung zu Authentik (Bonus-Artikel) gemacht.
Spätere Anpassungen
Diese Datei wirst du im Laufe der Serie noch häufiger anfassen – jedes Mal, wenn eine neue App dazukommt, ergänzt du in
access_control.ruleseinen Eintrag mit der neuen Subdomain. Den gleichen Weg nutzen wie jetzt: VS Codium öffnen und Datei bearbeiten, oder per SSHsudo nano /srv/docker/stacks/authelia/config/configuration.yml. Dankwatch: trueübernimmt Authelia Änderungen sofort, ohne dass du den Stack redeployen musst.
8. Den ersten User anlegen
Authelia lädt User aus users_database.yml. Die Datei brauchen wir noch nicht beim ersten Start (Watch-Modus fängt sie später ab) – aber wir legen sie direkt mit an, damit du dich sofort einloggen kannst.
8.1 Passwort-Hash erzeugen
Authelia bringt einen Hash-Generator mit, den wir per Einmal-Container nutzen:
docker run --rm -it authelia/authelia:4.39 \
authelia crypto hash generate argon2 \
--password 'DEIN-STARKES-PASSWORT-HIER'
Erwartung: Eine Ausgabe wie
Digest: $argon2id$v=19$m=65536,t=3,p=4$Hjc8e7WYcBFcJmEDUOsS9A$ozM7RyZR1EyDR8cuyVpDDfmLrGPGFgo5E2NNqRumui4
Den kompletten String ab $argon2id$… kopieren – das ist der gehashte Wert, der gleich in die User-Datei kommt.
Wähle ein wirklich starkes Passwort
Hier ist keine Master-Hygiene-Lektion nötig: 20+ Zeichen aus deinem Passwort-Manager, einmal generieren, fertig. Dieses Passwort schützt nachher den Zugriff auf Vaultwarden, Immich, Paperless und alles Weitere gleichzeitig – ein gutes Passwort hier ist sehr viel wertvoller als drei mittelmäßige in den Apps selbst.
8.2 users_database.yml anlegen
Gleicher Weg wie bei der configuration.yml – per VS Codium oder per SSH:
sudo nano /srv/docker/stacks/authelia/config/users_database.yml
Inhalt:
---
users:
martin:
disabled: false
displayname: 'Martin'
password: '$argon2id$v=19$m=65536,t=3,p=4$Hjc8e7WYcBFcJmEDUOsS9A$ozM7RyZR1EyDR8cuyVpDDfmLrGPGFgo5E2NNqRumui4'
email: 'martin@deine-domain.de'
groups:
- 'admins'
Das password-Feld bekommt den Hash aus Schritt 8.1 – nicht das Klartext-Passwort.
Wichtig: YAML ist whitespace-sensitiv. Nur Leerzeichen zum Einrücken, keine Tabs. Sonst quittiert Authelia beim Start mit:
could not parse the YAML database: yaml: line X: found character that cannot start any token
Die Gruppe admins ist genau die Gruppe, die in der configuration.yml per subject: ['group:admins'] als Filter genutzt wird. Spätere User für Family/Freunde lässt du einfach weg aus dieser Gruppe – sie kommen dann durch Login + 2FA bis zu den Subdomains ohne Admin-Filter (z. B. das geteilte Wiki), aber nicht auf Vaultwarden oder Paperless.
9. Stack in Komodo anlegen
Mit den Konfigurationsdateien auf dem Server geht es jetzt in Komodo weiter. Ab hier bis zum Deploy bleibt alles im Browser – kein Wechsel mehr.
In Komodo (http://vps:9120 oder über die Tailscale-IP):
- Links Stacks → oben rechts + New Stack
- Name:
authelia - Server:
vps-homelab - Stack Type:
UI Defined - Create
Im Stack-Editor pflegst du gleich compose.yaml (Sektion 10) und .env (Sektion 11). Beide Dateien greifen über env_file: .env und Volume-Mounts auf die in Sektion 6–8 vorbereiteten Verzeichnisse und Konfigurationsdateien zu.
10. compose.yaml
Im Compose-Editor:
name: authelia
services:
authelia:
image: authelia/authelia:latest
container_name: authelia
restart: unless-stopped
env_file: .env
volumes:
- ./config:/config
- ./data:/data
- ./log:/log
healthcheck:
test: ["CMD", "authelia", "healthcheck"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
depends_on:
redis:
condition: service_healthy
networks:
- authelia_internal
- homelab_net
redis:
image: redis:7-alpine
container_name: authelia-redis
restart: unless-stopped
command: ["redis-server", "--requirepass", "${AUTHELIA_REDIS_PASSWORD}"]
env_file: .env
volumes:
- ./redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${AUTHELIA_REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- authelia_internal
networks:
authelia_internal:
homelab_net:
external: true
Was passiert hier?
| Block | Erklärung |
|---|---|
image: authelia/authelia:4.39 |
Pinning auf Minor-Version. Patch-Updates innerhalb 4.39.x kommen automatisch, ein potentieller Sprung auf 4.40 wäre eine bewusste Entscheidung – analog postgres:17 und caddy-crowdsec:2 |
env_file: .env |
Sämtliche Secrets und Domain-Werte lädt Authelia aus der .env, nicht hardcoded ins Image |
./config:/config |
Hier liegen configuration.yml und users_database.yml – die Konfigurations-Quelldateien |
./data:/data |
SQLite-Datenbank und Notification-State. Das ist das Verzeichnis, das du regelmäßig sicherst |
./log:/log |
Authelia-Logs als Datei – vorbereitet für CrowdSec-Acquisition in Artikel 10 |
authelia_internal |
Internes Netz für Authelia ↔ Redis. Redis ist von außen unerreichbar, auch nicht von anderen Apps im homelab_net |
homelab_net |
Nur Authelia hängt mit drin, damit Caddy es als authelia:9091 erreichen kann |
Redis requirepass |
Redis akzeptiert nur Verbindungen mit Passwort – auch im internen Netz |
Kein ports: |
Beide Container sind ausschließlich intern erreichbar |
Warum Redis?
Authelia kann grundsätzlich auch ohne Redis laufen – nutzt dann In-Memory-Session-Storage. Praktischer Effekt: Jedes Mal, wenn du den Authelia-Container neu startest (z. B. nach einem Update oder Stack-Redeploy), sind alle aktiven Sessions weg. Du wirst überall ausgeloggt und musst dich neu einloggen – Vaultwarden, Immich, Paperless, alles.
Bei monatlichen Updates ist das ein laufender, kleiner Ärger. Redis als Session-Store (ca. 10 MB Container) löst das: Sessions überleben Container-Restarts, „Remember me" funktioniert zuverlässig. Es ist auch die offizielle Empfehlung von Authelia für produktive Setups.
Pfade konsistent mit Artikel 07: alles unter /srv/docker/stacks/authelia/, von Komodo selbst angelegt.
11. .env
Im .env-Tab:
# Hauptdomain und Auth-Subdomain
AUTHELIA_DOMAIN=deine-domain.de
AUTHELIA_URL=https://auth.deine-domain.de
# TOTP-Issuer – erscheint später als App-Name in der Authenticator-App
AUTHELIA_TOTP_ISSUER=deinedomain.de
# Secrets aus Schritt 6
AUTHELIA_JWT_SECRET=<JWT-SECRET-AUS-OPENSSL>
AUTHELIA_SESSION_SECRET=<SESSION-SECRET-AUS-OPENSSL>
AUTHELIA_STORAGE_ENCRYPTION_KEY=<STORAGE-ENC-KEY-AUS-OPENSSL>
AUTHELIA_REDIS_PASSWORD=<REDIS-PASSWORD-AUS-OPENSSL>
# Mail-Versand: Sender-Adresse für Authelia-Mails
AUTHELIA_SMTP_SENDER="Authelia <noreply@deine-domain.de>"
# Zeitzone
TZ=Europe/Berlin
Authelia liest diese Werte über Template-Variablen in der configuration.yml (kommt im nächsten Schritt) – kein extra Setup nötig.
Auszutauschen sind:
deine-domain.de→ deine tatsächliche Hauptdomain (vier Stellen:AUTHELIA_DOMAIN,AUTHELIA_URL,AUTHELIA_TOTP_ISSUER,AUTHELIA_SMTP_SENDER)- Die vier Secret-Platzhalter
<...-AUS-OPENSSL>→ die in Schritt 5 generierten Werte
Die Zeitzone passt für die meisten Self-Hoster, kann aber bei Bedarf angepasst werden.
12. Update-Verhalten konfigurieren
Bevor du deployst, zwei Schalter im Stack-Editor:
- Poll for Updates:
on - Auto Update:
off
Standard für alle Stacks in dieser Serie – Komodo zeigt dir an, wenn ein neues Image verfügbar ist, deployt aber nicht automatisch.
13. Deploy und Logs prüfen
In Komodo im Stack-Overview: Deploy klicken.
Komodo schreibt alle Dateien auf den Host, zieht beide Images und startet die Container. In Komodo öffnen → Tab Containers:
authelia-redissollte nach ~10 SekundenUp (healthy)zeigenautheliastartet erst nach Redis (durchdepends_on) und sollte nach ~30 Sekunden ebenfallsUp (healthy)zeigen
Erwartete Schlüsselzeilen in den Authelia-Logs:
level=info msg="Authelia v4.39.x is starting"
level=info msg="Startup checks completed successfully"
level=info msg="Listening for non-TLS connections on '0.0.0.0:9091'"
14. Caddy einbinden: die Auth-Subdomain und das (auth)-Snippet
Jetzt der entscheidende Schritt: Authelia ans Caddy hängen. Dafür ergänzt du im Caddyfile (Stack caddy, Tab Additional Files → Caddyfile) zwei Sachen.
14.1 Das (auth)-Snippet definieren
Direkt nach dem (public)-Snippet aus Artikel 07:
# ============================================================
# Snippet: Authelia Forward-Auth für persönliche Apps
# Wird per `import auth` in jeder geschützten Site verwendet
# ============================================================
(auth) {
forward_auth authelia:9091 {
uri /api/authz/forward-auth?authelia_url=https://auth.deine-domain.de/
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
}
Was passiert hier?
forward_auth authelia:9091– Caddy fragt vor jedem Request bei Authelia anuri /api/authz/forward-auth– genau der Endpoint, den wir inconfiguration.ymlaktiviert haben (server.endpoints.authz.forward-auth)?authelia_url=https://auth.deine-domain.de/– sagt Authelia, wohin der Browser für den Login-Flow umgeleitet wirdcopy_headers Remote-User Remote-Groups Remote-Email Remote-Name– Wenn die Auth erfolgreich war, hängt Caddy diese Header an den Request, bevor er an die App geht. Apps wie Immich oder Paperless können das auswerten (z. B. um den User-Namen anzuzeigen)
14.2 Die Auth-Subdomain selbst
Direkt nach dem test-Block den Login-Portal-Block ergänzen:
# Authelia Login-Portal – muss öffentlich erreichbar sein
auth.deinedomain.de {
import public
reverse_proxy authelia:9091
}
Hier kein import auth – das wäre eine Endlosschleife, weil das Portal selbst nicht hinter sich selbst sitzen kann. Deshalb steht es in access_control auch auf bypass.
14.3 Caddy neu laden
Validieren und reloaden – Pattern aus Artikel 07:
docker exec caddy caddy validate --config /etc/caddy/Caddyfile
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
Im Browser öffnen:
https://auth.deine-domain.de
Erwartung: Du siehst das Authelia-Login-Portal mit echtem Let's-Encrypt-Zertifikat. Schloss-Symbol klickbar → Aussteller Let's Encrypt.
15. Erster Login und 2FA einrichten
15.1 Login
Auf https://auth.deine-domain.de:
- Username:
martin(oder wie auch immer du den ersten User genannt hast) - Password: das Klartext-Passwort, das du in Schritt 10.1 zum Hashen genutzt hast
Du landest auf dem User-Portal. Hier kannst du jetzt 2FA einrichten – bevor du es brauchst.
15.2 TOTP einrichten
Im User-Portal auf Settings → Two-Factor Authentication → Set up One-Time Password.
Authelia schickt dir jetzt eine Bestätigungs-Mail an die Adresse aus users_database.yml – das ist der Moment, an dem dein Mail-Relay aus Artikel 08 erstmals produktiv zum Einsatz kommt.
Im Mail-Postfach erscheint eine Mail mit einem Bestätigungslink. Klick drauf → zurück im Browser zeigt Authelia einen QR-Code.
QR-Code mit deiner Authenticator-App scannen. Der Code erscheint unter dem Namen deine-domain.de – das ist der TOTP_ISSUER aus deiner .env.
Die App zeigt dir einen 6-stelligen Code, der sich alle 30 Sekunden ändert. Diesen Code im Setup-Formular eingeben → fertig.
Mail kommt nicht an?
Prüfe in dieser Reihenfolge:
- Mail-Relay-Container läuft? In Komodo: Container
mail-relay→Up.- Logs des Mail-Relays in Komodo zeigen einen Versuch? Wenn nichts kommt, erreicht Authelia den Relay nicht – beide müssen im
homelab_netsein.- Mail im Spam-Ordner? Bei der ersten Mail von einer neuen Absender-Adresse passiert das oft.
- Bei wirklichem Versagen:
notifier.filesystemals Alternative inconfiguration.yml– schreibt die Mails als Textdateien nach/data/notification.txt. Dann kannst du den Bestätigungslink dort rauskopieren. Aber das ist die Notlösung, nicht der Normalbetrieb.
15.3 Test: Login-Flow vollständig durchlaufen
Inkognito-Browser auf https://auth.deine-domain.de:
- Username + Passwort → Login klappt
- Bei einer geschützten App (z. B. später
vault.deine-domain.de) wirst du zum 2FA-Schritt geleitet - TOTP-Code aus der Authenticator-App eingeben
- → Weiterleitung zur App
15.4 Test: Session überlebt Container-Restart
Das ist der konkrete Mehrwert von Redis. Probier es einmal aus:
docker restart authelia
Anschließend im Browser eine geschützte Subdomain aufrufen – du bist nicht ausgeloggt. Ohne Redis wärst du an dieser Stelle wieder auf dem Login-Portal gelandet.
Optional: Passkeys statt 6-stelligem Code
Neben TOTP unterstützt Authelia auch WebAuthn – das ist die Technik hinter Passkeys (z. B. im Apple-/Google-Konto), Hardware-Sticks wie YubiKey, oder Touch ID auf dem Mac. Statt einen Code abzutippen, bestätigst du den Login per Fingerabdruck, Face ID oder USB-Stick. Vorteil: niemand kann dich auf einer gefälschten Login-Seite austricksen, weil der Browser den echten Server überprüft. Im User-Portal unter Set up WebAuthn einrichten.
Empfehlung: TOTP als Backup behalten (für den Fall, dass der Hardware-Stick mal nicht zur Hand ist), Passkeys/WebAuthn als bequeme Standard-Methode hinzufügen.
16. Wie das in den App-Artikeln aussehen wird
Damit du weißt, was du in Phase 3 erwartet: Jede geschützte App wird im Caddyfile in genau einem Block landen, mit zwei Imports:
# Vaultwarden (Artikel 12)
vault.deine-domain.de {
import public
import auth
reverse_proxy vaultwarden:80
}
# Immich (Artikel 13)
immich.deine-domain.de {
import public
import auth
reverse_proxy immich-server:2283
}
# Paperless-ngx (Artikel 14)
docs.deine-domain.de {
import public
import auth
reverse_proxy paperless:8000
}
Drei Dinge passieren mit import auth:
- Vor jedem Request fragt Caddy bei Authelia an
- Bei ungültigem Cookie → Redirect auf das Login-Portal
- Bei gültigem Cookie + passender Policy → Request geht durch zur App, mit User-Headern
Plus: Die Domain muss in access_control.rules der configuration.yml stehen – sonst greift die default_policy: one_factor, was nur Passwort-Schutz bedeutet. Für sensible Apps trägst du die Subdomain mit two_factor + subject: ['group:admins'] ein, und Authelia liest die Datei dank watch: true automatisch neu.
17. Erweiterungs-Pfade
Weitere User anlegen
Pro neuem User: Passwort-Hash erzeugen (Schritt 10.1), Eintrag in users_database.yml ergänzen – dank watch: true wird der Account sofort gültig. Wenn der User keine Admin-Rechte bekommen soll: Gruppe admins weglassen. Er kommt dann durch Login + 2FA bis zu den Subdomains ohne Admin-Filter, aber nicht auf die sensiblen Apps.
Mehr Apps
Im access_control.rules-Block die Subdomain einfügen, Policy und ggf. subject festlegen. Authelia liest die Datei live nach – kein Stack-Restart nötig.
Wenn das nicht mehr reicht: Authentik (Bonus)
Drei Anzeichen, dass du über Authelia hinausgewachsen bist:
- Du willst Apps wie Komodo oder Immich per OIDC-SSO anmelden (Authelia ist nur Forward-Auth-Proxy, kein OIDC-Provider für die App-internen Logins).
- Du betreibst mehr als ~5 User und willst Rollen-/Gruppen-Verwaltung mit echter UI.
- Du brauchst LDAP/SAML-Integration für andere Tools.
Dann lohnt sich der Wechsel zu Authentik. Der Bonus-Artikel im Anschluss an diese Serie zeigt, wie du Authelia in-place gegen Authentik tauschst, ohne den Rest der Infrastruktur anzufassen – Caddy bleibt, der (auth)-Snippet wird nur leicht umgeschrieben, der Rest läuft weiter wie zuvor.
Für 95 % der Self-Hosting-Setups ist Authelia ausreichend und bleibt es auch.
Und jetzt?
Phase 2 ist damit für die Pflicht-Bausteine durch. Du hast eine Auth-Schicht mit 2FA, die in Phase 3 vor jede persönliche App gestellt wird – mit einer einzigen Zeile (import auth) pro Caddy-Block.
Wer den optionalen Sicherheitsblock noch dazunehmen will, macht jetzt Artikel 10 (CrowdSec) weiter. CrowdSec ergänzt Authelia gut: Authelia schützt das Login-Formular vor Brute Force auf Account-Ebene (über regulation), CrowdSec auf IP-Ebene über alle Apps hinweg. Die Authelia-Logs liegen dank ./log:/log schon im richtigen Format vor – CrowdSec liest sie direkt mit. Das CrowdSec-Plugin steckt seit Artikel 07 schon im Caddy-Image, nur eingeschaltet wird es noch nicht.
Wer das überspringen will, geht direkt zu Artikel 11 (App-Baukasten) – dem Bindeglied zwischen Infrastruktur und den ersten echten Apps in Phase 3.
👉 Nächster Artikel: 10. CrowdSec – Angriffserkennung für öffentliche Web-Dienste (optional)
👉 Oder direkt: 11. App-Baukasten verstehen und nutzen
Discussion