Install via Docker

docker run -d \
    --name authelia \
    --restart unless-stopped \
    -p 127.0.0.1:9091:9091 \
    -v ./config:/config \
    authelia/authelia:latest

Configuration (single-file)

./config/configuration.yml:

theme: dark
default_2fa_method: totp
log: { level: info }

server:
  address: 'tcp://0.0.0.0:9091/'

totp:
  issuer: lab.example.com
  algorithm: sha1

session:
  secret: <random-32-chars>
  expiration: 1h
  inactivity: 10m
  remember_me: 1M
  cookies:
    - domain: lab.example.com
      authelia_url: https://auth.lab.example.com
      default_redirection_url: https://lab.example.com

storage:
  encryption_key: <random-32-chars>
  local:
    path: /config/db.sqlite3

# Authentication backend — users + groups
authentication_backend:
  file:
    path: /config/users_database.yml
    password:
      algorithm: argon2
      argon2: { variant: argon2id, iterations: 3, memory: 65536, parallelism: 4 }

# Per-resource access rules
access_control:
  default_policy: deny
  rules:
    - domain: "public.lab.example.com"
      policy: bypass

    - domain: "*.lab.example.com"
      policy: one_factor       # username/password OK

    - domain: "admin.lab.example.com"
      subject: "group:admin"
      policy: two_factor       # require TOTP/WebAuthn

    - domain: "vault.lab.example.com"
      subject: "group:admin"
      policy: two_factor
      methods: [GET, POST]

# Notifications (for 2FA setup emails)
notifier:
  smtp:
    username: authelia@example.com
    password: <pw>
    host: smtp.example.com
    port: 587
    sender: "Authelia <authelia@example.com>"

Users database (file backend)

# Generate Argon2id password hash
docker run --rm authelia/authelia:latest authelia crypto hash generate \
    argon2 --password 'mySecretPassword'

# ./config/users_database.yml
users:
  amir:
    displayname: "Amir Eslampanah"
    password: "$argon2id$v=19$m=65536,t=3,p=4$..."
    email: amir@example.com
    groups: [admin, users]

  bob:
    displayname: "Bob"
    password: "$argon2id$v=19$..."
    email: bob@example.com
    groups: [users]

For LDAP / Active Directory backend, configure authentication_backend.ldap instead. Authelia validates against the directory at login.

Wire to Caddy (forward-auth)

# Caddyfile
auth.lab.example.com {
    reverse_proxy 127.0.0.1:9091
}

# Protected app
grafana.lab.example.com {
    forward_auth 127.0.0.1:9091 {
        uri /api/verify?rd=https://auth.lab.example.com/
        copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
    }
    reverse_proxy 127.0.0.1:3000
}

prometheus.lab.example.com {
    forward_auth 127.0.0.1:9091 {
        uri /api/verify?rd=https://auth.lab.example.com/
        copy_headers Remote-User Remote-Groups
    }
    reverse_proxy 127.0.0.1:9090
}

The flow: user visits grafana.lab.example.com; Caddy calls Authelia; if not authenticated, Caddy redirects to auth.lab.example.com; user logs in; Authelia sets a session cookie scoped to *.lab.example.com; future requests to any subdomain skip the redirect.

Wire to nginx

# Auth portal
server {
    server_name auth.lab.example.com;
    listen 443 ssl;
    location / { proxy_pass http://127.0.0.1:9091; }
}

# Protected app
server {
    server_name grafana.lab.example.com;
    listen 443 ssl;

    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-Forwarded-Method $request_method;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Uri $request_uri;
    }

    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;

        proxy_set_header Remote-User $user;
        proxy_set_header Remote-Groups $groups;

        error_page 401 =302 https://auth.lab.example.com/?rd=$target_url;
        proxy_pass http://127.0.0.1:3000;
    }
}

Wire to Traefik (the cleanest)

# traefik.yml middleware
http:
  middlewares:
    authelia:
      forwardAuth:
        address: "http://authelia:9091/api/verify?rd=https://auth.lab.example.com/"
        trustForwardHeader: true
        authResponseHeaders:
          - "Remote-User"
          - "Remote-Groups"
          - "Remote-Name"
          - "Remote-Email"

# Per-service: traefik.http.routers.grafana.middlewares=authelia@file

Trusted header forwarding to the app

Once authenticated, Authelia forwards the user's identity via Remote-User, Remote-Groups, Remote-Name, Remote-Email headers. Apps that understand "trusted-header" auth (Grafana, Gitea / Forgejo, Jellyfin, Plex) can consume these directly. For apps that don't, they still get auth without knowing about Authelia.

OIDC support (the bridge mode)

Authelia 4.30+ can also act as a (limited) OIDC provider. For apps that need OIDC but can't do trusted-header, add:

identity_providers:
  oidc:
    hmac_secret: <random>
    issuer_private_key: <contents of key.pem>
    clients:
      - id: grafana
        secret: '$pbkdf2-sha512$...'        # generate via authelia crypto hash
        public: false
        authorization_policy: two_factor
        redirect_uris:
          - https://grafana.lab.example.com/login/generic_oauth
        scopes: [openid, groups, email, profile]
        grant_types: [authorization_code]
        response_types: [code]
        response_modes: [form_post]

Now Grafana can be configured with OIDC pointing at Authelia. Mix-and-match: forward-auth for some apps + OIDC for others.

2FA setup flow

First login → Authelia prompts to register TOTP. User scans the QR code with Authy / 1Password / Google Authenticator; sets a name. Subsequent logins ask for password + TOTP. WebAuthn / FIDO2 works the same way under "Methods" in the user portal — tap a security key once to enroll.

For per-resource policy two_factor, login forces 2FA before access. For one_factor, password is enough.

Authelia vs alternatives

  • Authentik (see that tutorial) — full IdP with own UI / flows / user store. Use when you want SAML / SCIM / complex policy.
  • Keycloak — the heavyweight; massive feature surface; Java-heavy.
  • Dex (see that tutorial) — OIDC-only; federates upstream; no UI / sessions for forward-auth.
  • oauth2-proxy — the older forward-auth tool; great if you already use an upstream IdP and just need to enforce auth at the reverse proxy.
  • Caddy security plugin — for Caddy-only setups; tighter integration but Caddy-locked.

When Authelia is the right pick

  • "I have 5-30 self-hosted apps + want to put SSO + 2FA in front of them with minimum operational drama."
  • Forward-auth is the architecture (reverse proxy already in place).
  • You don't need full IdP features (SAML, SCIM, complex auth flows).

When it isn't

  • Need to be an IdP for other systems — Authentik / Keycloak.
  • Need centralized user management across many orgs / tenants — full IdP.
  • SAML SP / IdP — Authentik / Keycloak.