Why an authentication gateway in the reverse proxy
Without a centralized gateway, each service manages its own login: Grafana has its own user database, Nextcloud has its own, Portainer has its own. The result: different passwords everywhere, no consistent MFA, and a multiplied attack surface. Authelia solves this by intercepting the HTTP flow: your reverse proxy (Nginx, Traefik, Caddy) submits each request to it before relaying it, and Authelia decides — based on identity, group, requested path, and required MFA level — whether the request should pass. The protected application only sees an authenticated request with injected identity headers (Remote-User, Remote-Groups). It doesn't need to manage authentication itself.
What self-hosted Authelia gives you
- Centralized authentication for all your services without modifying their code
- Native MFA: TOTP (Google Authenticator, Aegis), WebAuthn/passkey (hardware keys, Touch ID, Windows Hello), Duo
- OIDC provider in production since v4.34: your OIDC-compatible apps delegate auth to Authelia
- Granular access control by domain, path, user group, and authentication level
- Native integration with Traefik (forwardAuth), Nginx (auth_request), Caddy (forward_auth)
- LDAP/Active Directory support for team management, compatible with LLDAP
- Single Go binary (~30 MB), lightweight Docker image, low memory footprint
- Structured JSON logs and Prometheus metrics for observability
Prerequisites
Before deploying Authelia, verify that your environment is ready. You need a Linux VPS with Docker and Docker Compose installed, an existing reverse proxy (Traefik v2/v3 or Nginx), a domain name with a valid TLS certificate (Let's Encrypt recommended), and a Redis instance accessible by Authelia to store sessions (required in production for persistence and scalability). For user storage, you can start with a local YAML file or connect an LDAP server like LLDAP. Authelia also requires a JWT key, a session encryption key, and an OIDC secret if you enable that mode: generate them with openssl rand -hex 32.
Deploy Authelia with Docker Compose
Create the directory structure
Create
/opt/authelia/config/for configuration files and/opt/authelia/data/for SQLite storage and keys. Set appropriate permissions:mkdir -p /opt/authelia/{config,data} && chown -R 1000:1000 /opt/authelia.Write the configuration.yml file
Create
/opt/authelia/config/configuration.yml. Definedefault_redirection_url,jwt_secret,session.secret,storage.encryption_key, configure the authentication backend (file or LDAP), Redis for sessions, and the default access policy (denyrecommended).Create the users_database.yml file (file backend)
For a quick start, create
/opt/authelia/config/users_database.ymlwith your users. Passwords must be hashed with Argon2id:docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password 'YourPassword'. Copy the generated hash into the file.Configure Redis for sessions
Add Redis to your
docker-compose.yml. Inconfiguration.yml, setsession.redis.host: redisandsession.redis.port: 6379. Redis is essential for sessions to survive Authelia restarts.Write the docker-compose.yml
Define the
authelia,redisservices, and if you use Traefik, add the appropriate labels. Mount/opt/authelia/configread-only and/opt/authelia/dataread-write. Expose port 9091 internally, never directly to the Internet.Configure Traefik with forwardAuth
Create a Traefik middleware pointing to
http://authelia:9091/api/authz/forward-auth. Apply this middleware to routers of services to protect via the labeltraefik.http.routers.<service>.middlewares: authelia@docker. For Nginx, useauth_request /autheliawith the appropriateproxy_passdirectives.Configure the session domain
In
configuration.yml, setsession.domainto your root domain (e.g.,mydomain.com) so the cookie is shared between subdomains. This is the most common cause of redirect loops: ifsession.domaindoesn't match the domain of the protected service, the cookie is never sent back.Start and validate
Start with
docker compose up -d. Check logs:docker compose logs -f authelia. Accesshttps://auth.mydomain.comto see the login portal. Test by accessing a protected service: you should be redirected to the portal, then sent back to the service after authentication.Enable the OIDC provider (optional)
Since v4.34, the OIDC provider is production-ready. Add the
identity_providers.oidcsection to your configuration with your clients (Nextcloud, Grafana, Gitea…). Each client has anid, asecret(hashed with Argon2id), and its allowedredirect_uris. Your OAuth2-compatible apps can then fully delegate authentication to Authelia.
Use Authelia as an OIDC provider for true SSO
OIDC provider mode turns Authelia into true SSO: the user logs in once on the Authelia portal, and all applications configured as OIDC clients retrieve the identity without asking for a password again. Nextcloud, Grafana, Gitea, Portainer, Outline… all support OIDC. Configure each client with client_id, client_secret, and point the discovery URL to https://auth.mydomain.com/.well-known/openid-configuration. The groups scope allows transmitting Authelia groups to client applications for role management.
Access control rules: concrete examples
The access_control section is the heart of Authelia's logic. Since v4.37, policies have been reworked. Here are representative examples. For public access without auth: {domain: 'status.mydomain.com', policy: bypass}. For strong MFA access restricted to admins: {domain: 'portainer.mydomain.com', subject: 'group:admins', policy: two_factor}. For an API path without auth (webhooks): {domain: 'n8n.mydomain.com', resources: ['^/webhook/.*'], policy: bypass}. For the rest of N8N with simple MFA: {domain: 'n8n.mydomain.com', policy: one_factor}. The default_policy: deny rule ensures that anything not explicitly listed is blocked. Rule order matters: the first matching rule applies. The subject field accepts user:alice, group:admins, or a combined list.
MFA: TOTP, WebAuthn/passkey and Duo
Authelia supports three second-factor methods, each with its advantages.
TOTP (Time-based One-Time Password) is the most universal method: the user scans a QR code with an app like Aegis (Android), Raivo (iOS), or Bitwarden Authenticator. A 6-digit code changes every 30 seconds. Simple to deploy, compatible with all devices, but vulnerable to phishing if the code is intercepted in transit.
WebAuthn/passkey is the most robust method, natively supported by Authelia since v4.36. The user registers a hardware key (YubiKey, Nitrokey) or a platform authenticator (Touch ID on macOS, Windows Hello, Face ID on iOS). Public-key cryptography makes phishing impossible: the private key never leaves the device. Authelia implements the full WebAuthn Level 2 spec, which includes synchronized passkeys (via iCloud Keychain or Google Password Manager) in addition to hardware keys.
Duo delegates the second factor to the Duo Security cloud service: mobile push, SMS, or phone call. Convenient for teams already using Duo, but introduces a dependency on a third-party service.
For most self-hosted stacks, the recommended combination is TOTP as the base method and WebAuthn as the preferred method for users who have a YubiKey or a recent Apple/Windows device.
Migrating to LLDAP: team management
The file backend (users_database.yml) works for one user or a small household, but becomes difficult to manage beyond 5-10 users. LLDAP (Light LDAP) is a lightweight LDAP implementation written in Rust, with a simple web interface, that integrates perfectly with Authelia.
Deploy LLDAP by adding its service to your docker-compose.yml with the LLDAP_JWT_SECRET and LLDAP_LDAP_BASE_DN variables (e.g., dc=mydomain,dc=com). Create your groups in the LLDAP interface (admins, users, devops…), then modify your configuration.yml to switch from the file backend to the ldap backend: define authentication_backend.ldap.url, base_dn, users_filter ((&(uid={input})(objectClass=person))), groups_filter, user (the LLDAP service account), and password.
Note the v4.35 breaking change: the field was called authentification_backend (with a typo) before v4.35 and was corrected to authentication_backend. Check your configuration if migrating from an earlier version.
Updating Authelia: avoiding breaking changes
Authelia follows strict semantic versioning and documents its breaking changes per version. Here are the notable changes to know for clean migrations.
v4.34: the OIDC provider goes to production. If you were using it in experimental mode, some options have been renamed.
v4.35: correction of the typo authentification_backend → authentication_backend. Any configuration using the old name must be updated before upgrading to v4.35+, otherwise Authelia refuses to start with a configuration validation error.
v4.37: rework of access_control policies. Rule syntax has evolved to be more explicit. Check the official changelog before upgrading from v4.36 or earlier.
v4.38.x (current version): stabilization, no major breaking change since v4.37.
Recommended update procedure: (1) read the changelog from your current version; (2) back up /opt/authelia/config/ and /opt/authelia/data/; (3) test the new configuration with docker run --rm -v /opt/authelia/config:/config authelia/authelia:4.38.x authelia validate-config before restarting; (4) update the tag in docker-compose.yml and restart. The validate-config command catches schema errors before deployment.
Monitoring: logs and Prometheus metrics
Authelia exposes its Prometheus metrics on /metrics (configurable port, default 9959). Enable them in configuration.yml with telemetry.metrics.enabled: true and telemetry.metrics.address: 'tcp://0.0.0.0:9959'. Don't expose this port publicly: only expose it to your internal monitoring stack.
Key metrics to monitor: authelia_authn_requests_total (total authentication attempts, labeled by success/failure), authelia_authn_duration_seconds (authentication latency), and underlying Redis metrics for session health.
For logs, Authelia supports structured JSON format (log.format: json) ideal for ingestion into Loki or a SIEM. Set log.level: info in production (avoid debug which logs full headers). Authentication logs include source IP, username, target service, and result — valuable for detecting brute-force attempts.
In Grafana, create an alert on an abnormal authentication failure rate (e.g., more than 10 failures in 5 minutes on the same user) to detect dictionary attacks before they succeed.
Authelia vs Authentik: which one to choose?
Scroll the table
| Criterion | Authelia | Authentik |
|---|---|---|
| Docker footprint | ~30 MB (Go binary) | ~1.5 GB (Python/Django + worker) |
| Minimum RAM | ~50 MB | ~512 MB recommended |
| OIDC provider | Yes, since v4.34 | Yes, mature and feature-rich |
| SAML provider | No | Yes |
| Admin interface | YAML configuration only | Full web interface |
| Application proxy | No | Yes (Authentik tunnels) |
| SCIM provisioning | No | Yes |
| Ideal use case | Lightweight stack, pure reverse proxy, config as code | Full IAM, enterprise SSO, SAML/SCIM needs |
Troubleshooting common errors
Infinite redirect loop. The user is bounced in a loop between the service and the Authelia portal without ever logging in. Most common cause: session.domain doesn't match the service's domain. If your service is on app.mydomain.com and session.domain is otherdomain.com, the session cookie is never sent back. Also check that the forwardAuth middleware is properly applied and that default_redirection_url points to a valid URL.
Cookie domain mismatch. The browser refuses to send the cookie back. Cause: session.domain must be the common parent domain (mydomain.com), not a subdomain. Verify with DevTools (Application tab > Cookies) that the authelia_session cookie has the correct domain.
Redis unreachable. Authelia starts but sessions don't persist, or the log shows Error connecting to Redis. Verify that the Redis container is on the same Docker network as Authelia, that host in configuration.yml matches the Docker service name (not localhost), and that Redis isn't protected by a password not configured on the Authelia side.
Invalid configuration on startup. Since v4.35, the configuration schema is strictly validated. Use authelia validate-config before any restart. Most common errors: authentification_backend (old typo), wrong YAML indentation, or missing OIDC field (client_secret must be hashed with Argon2id since v4.37).
MFA not prompted despite two_factor in rules. Check that the user has registered a second factor in their profile (https://auth.mydomain.com). Without a registered MFA device, Authelia may fall back to one_factor depending on your default_2fa_method configuration.
Final advice: start simple, evolve progressively
First deploy Authelia with the file backend, a single two_factor rule for your most exposed services, and TOTP as the MFA method. Once stable, migrate to LLDAP for team management, enable the OIDC provider for SSO, then add WebAuthn for users who have a hardware key or a device supporting passkeys. This progression prevents you from debugging multiple new components simultaneously and lets you validate each layer independently.