Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Authelia Ubuntu
GUIDEInstall Guides

How to Install Authelia on Ubuntu 24.04 VPS: Self-Hosted SSO + 2FA

23 min read

How to Install Authelia on Ubuntu 24.04 VPS — Self-Hosted SSO + 2FA for Your Entire Stack

If you run a self-hosted stack — Nextcloud, Grafana, Portainer, Vaultwarden, Gitea, a handful of internal dashboards — you have a problem. Every app has its own login. Every app has its own password policy. Half of them do not support two-factor authentication at all, and the ones that do force you to enroll separately. This guide walks you through installing Authelia on an Ubuntu 24.04 VPS, from the first docker compose up to a hardened production deployment that puts a single sign-on portal with strong 2FA in front of every service you host.

Not sure which plan to pick? Authelia itself is light, but the stack behind it (Redis, LDAP, your apps) benefits from real cores and RAM. CloudCore Professional gives you 6 vCPU, 12 GB RAM, and 100 GB NVMe — enough for Authelia plus a dozen protected services.

Table of Contents

  • What is Authelia?
  • Why Self-Host SSO + 2FA?
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Install Docker and Docker Compose
  • Step 3: Plan Your Domains
  • Step 4: Create the docker-compose.yml
  • Step 5: Generate Password Hashes
  • Step 6: Write users_database.yml
  • Step 7: Write configuration.yml
  • Step 8: Start Authelia
  • Step 9: Integrate with Nginx (Forward Auth)
  • Step 10: Alternative — Traefik Middleware
  • Step 11: Alternative — Caddy
  • Step 12: First Login and TOTP Enrollment
  • Step 13: WebAuthn Hardware Keys
  • Step 14: OIDC Provider (Nextcloud Example)
  • Step 15: SMTP for Password Resets and Alerts
  • Step 16: Banned Users and Regulation
  • Backups
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is Authelia?

    Authelia is an open-source authentication and authorization server that sits in front of your web apps and asks one question: is this request allowed? It is written in Go, ships as a single small binary (or Docker image), and talks the two protocols that matter for web SSO — forward authentication (for reverse proxies like Nginx, Traefik, Caddy, HAProxy) and OpenID Connect 1.0 (for apps that speak OIDC natively).

    The pieces you actually interact with are:

    • Web portal — a clean login page at auth.yourdomain.com where users enter their password and complete 2FA. After login, they are redirected back to whichever protected app they were trying to reach.
    • Authentication backend — either a flat file backend (a YAML file with bcrypt or Argon2 password hashes, perfect for 1-50 users) or LDAP (for larger directories, including lldap as a lightweight companion).
    • Access control rules — a declarative list that says "anyone on this network can reach Grafana with one factor, but /admin needs two factors and must be a member of the admins group." Domains and subdomains, path prefixes, networks, methods, and user groups are all matchable.
    • Second factor methods — TOTP (Google Authenticator, Aegis, 1Password, Bitwarden), WebAuthn (YubiKey, Solokey, Touch ID, Windows Hello), and Duo push.
    • Session storage — Redis in production, with a secure httpOnly cookie scoped to your root domain so every subdomain inherits the session.
    • Persistence — SQLite for small deployments or PostgreSQL / MySQL for production — stores TOTP secrets, WebAuthn credentials, user preferences, and audit logs.
    • OIDC provider — lets apps like Nextcloud, Grafana, Outline, Gitea, or Proxmox delegate login to Authelia via standard OAuth 2.0 / OIDC flows.
    Upstream: github.com/authelia/authelia. Docs: authelia.com/overview.

    Why Self-Host SSO + 2FA?

    The commercial SSO market is aimed at companies with hundreds of seats. Okta, Auth0, and JumpCloud start in the low double digits per user per month — fine for a 200-person SaaS, overkill for a home lab or a two-person agency running a stack of self-hosted tools. Here is what you get by running Authelia on your own VPS:

    • One login, dozens of apps — enroll 2FA once. Log in once. Every protected subdomain inherits the session until it expires.
    • 2FA on apps that do not support it — Portainer, Vaultwarden's admin panel, Uptime Kuma, Home Assistant, every "we will add SSO in v2" tool ever. Authelia forces 2FA in front of the app, whether the app knows about it or not.
    • Zero per-user cost — the container runs for the same EUR 19.99/mo whether you have one user or fifty. No seat math.
    • Data stays on your VPS — passwords, TOTP secrets, WebAuthn credentials, and session cookies never leave your server. No third-party identity provider sees any of it.
    • OIDC for modern apps, forward-auth for everything else — you do not have to pick. Nextcloud talks OIDC; your random Python dashboard gets forward-auth. Both use the same user database and the same 2FA enrollment.
    • Hardware keys are free — WebAuthn/FIDO2 with YubiKey or Solokey costs nothing to enable. Commercial IdPs often put hardware 2FA behind their Enterprise tier.
    • Auditable open source — readable Go code, public security advisories, a clear threat model.

    Authelia vs. Alternatives

    ToolModelBest ForDownsides
    AutheliaForward-auth + OIDCSmall-to-medium self-hosted stacks, reverse-proxy loversYAML config, no built-in user portal for self-service beyond 2FA
    KeycloakFull IdP (SAML, OIDC, LDAP)Enterprises, SAML requirements, admin UIHeavy (JVM, 1 GB+ RAM), steeper setup
    AuthentikFull IdP with flows UITeams that want a GUI for everythingLarger footprint, more moving parts
    Pocket IDPasskey-onlyWebAuthn-first deploymentsNewer, narrower protocol support
    For a VPS hosting self-hosted apps behind Nginx or Traefik, Authelia hits the sweet spot: small, declarative, strong 2FA, and it plays nice with reverse proxies.

    Prerequisites

    Before you begin, make sure you have:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access
    • SSH access to your server
    • A registered domain with DNS you control (Cloudflare, Route 53, or similar) — you need at least auth.yourdomain.com and one app subdomain like app.yourdomain.com
    • Docker and Docker Compose installed — see How to Install Docker on Ubuntu 24.04 and How to Install Docker Compose on Ubuntu 24.04
    • A reverse proxy already set up — see How to Install Nginx on Ubuntu 24.04 or How to Install Traefik on Ubuntu 24.04
    • At least 1 GB of free RAM (Authelia itself uses ~50 MB; Redis adds another ~20 MB)
    • Valid TLS certificates for your domains — Let's Encrypt via Certbot is fine
    Recommended Plan: CloudCore Professional
    >
    If you are standing up a new self-hosted stack from scratch, we recommend CloudCore Professional:
    >
    - 6 vCPU cores
    - 12 GB RAM
    - 100 GB NVMe SSD
    - Unmetered bandwidth
    - EUR 19.99/month
    >
    This gives you enough headroom for Authelia + Redis + Postgres + five to ten protected apps in Docker with breathing room for backups and log retention.

    Connect to your server:

    bash
    ssh root@your-server-ip

    Step 1: Update System Packages

    bash
    sudo apt update && sudo apt upgrade -y

    Reboot if the kernel was updated:

    bash
    sudo reboot

    Step 2: Install Docker and Docker Compose

    If Docker is not already installed, follow our dedicated guides:

    • How to Install Docker on Ubuntu 24.04
    • How to Install Docker Compose on Ubuntu 24.04
    Verify both are working:

    bash
    docker --version
    docker compose version

    Expected output:

    text
    Docker version 27.3.1, build ce12230
    Docker Compose version v2.29.7

    Step 3: Plan Your Domains

    Authelia assumes a single root domain shared by the portal and every protected app. The session cookie is set at the root domain so every subdomain gets it. Typical layout:

    SubdomainWhat it serves
    auth.yourdomain.comAuthelia portal
    nextcloud.yourdomain.comNextcloud (OIDC client)
    grafana.yourdomain.comGrafana (forward-auth protected)
    portainer.yourdomain.comPortainer (forward-auth protected)
    Point all of them (wildcard *.yourdomain.com works fine) at your VPS via A or CNAME records. Authelia itself does not issue TLS — your reverse proxy does.

    Step 4: Create the docker-compose.yml

    Create a working directory:

    bash
    sudo mkdir -p /opt/authelia/{config,secrets,redis}
    cd /opt/authelia

    Create the Compose file:

    bash
    sudo tee /opt/authelia/docker-compose.yml > /dev/null <<'EOF'
    services:
      authelia:
        image: authelia/authelia:4.38
        container_name: authelia
        restart: unless-stopped
        networks:
          - proxy
          - internal
        ports:
          - "127.0.0.1:9091:9091"
        volumes:
          - ./config:/config
          - ./secrets:/secrets:ro
        environment:
          - TZ=Europe/Berlin
          - AUTHELIA_JWT_SECRET_FILE=/secrets/jwt_secret
          - AUTHELIA_SESSION_SECRET_FILE=/secrets/session_secret
          - AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE=/secrets/storage_encryption_key
          - AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET_FILE=/secrets/reset_password_jwt_secret
          - AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE=/secrets/smtp_password
        depends_on:
          - redis
        healthcheck:
          test: ["CMD", "authelia", "healthcheck"]
          interval: 30s
          timeout: 5s
          retries: 3

    redis: image: redis:7-alpine container_name: authelia-redis restart: unless-stopped networks: - internal volumes: - ./redis:/data command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"]

    networks: proxy: external: true internal: driver: bridge EOF

    Create the proxy network if your reverse proxy (Nginx container, Traefik) does not already own one:

    bash
    docker network create proxy

    If you run Nginx on the host instead of in a container, drop the proxy network from the Authelia service and keep only the 127.0.0.1:9091 port mapping — Nginx will reach Authelia at http://127.0.0.1:9091.

    Generate the Secrets

    Authelia refuses to start without JWT, session, and storage encryption secrets. Generate four strong ones:

    bash
    cd /opt/authelia/secrets

    128-char random strings (Authelia recommends 64+ for these)

    openssl rand -hex 64 | sudo tee jwt_secret > /dev/null openssl rand -hex 64 | sudo tee session_secret > /dev/null openssl rand -hex 64 | sudo tee storage_encryption_key > /dev/null openssl rand -hex 64 | sudo tee reset_password_jwt_secret > /dev/null

    SMTP password (replace with your actual app password)

    echo 'your-smtp-app-password' | sudo tee smtp_password > /dev/null

    sudo chmod 600 /opt/authelia/secrets/*

    Step 5: Generate Password Hashes

    Authelia stores passwords as Argon2id hashes in the user database. Generate one using the official image — no need to install Authelia on the host:

    bash
    docker run --rm authelia/authelia:4.38 \
      authelia crypto hash generate argon2 --password 'YourStrongPassword123!'

    Expected output:

    text
    Digest: $argon2id$v=19$m=65536,t=3,p=4$BpLnfgDsc2WD8F2q$o/vzA4myCqZZ36bUGsDY//8mKUYNZZaR0t4MFFSs+iM

    Copy the entire $argon2id$... line — you will paste it into users_database.yml in the next step. Repeat for every user you want to create. Never reuse hashes between users.

    If you prefer bcrypt (also supported), swap argon2 for bcrypt in the command.

    Step 6: Write users_database.yml

    bash
    sudo tee /opt/authelia/config/users_database.yml > /dev/null <<'EOF'
    users:
      alice:
        disabled: false
        displayname: "Alice Admin"
        password: "$argon2id$v=19$m=65536,t=3,p=4$BpLnfgDsc2WD8F2q$o/vzA4myCqZZ36bUGsDY//8mKUYNZZaR0t4MFFSs+iM"
        email: [email protected]
        groups:
          - admins
          - dev

    bob: disabled: false displayname: "Bob Developer" password: "$argon2id$v=19$m=65536,t=3,p=4$Yg8u9c/aG2u2eBYD$REPLACE_WITH_BOBS_HASH" email: [email protected] groups: - dev EOF sudo chmod 600 /opt/authelia/config/users_database.yml

    The groups list is what you reference from access_control rules. Common groups: admins, dev, ops, family, guests.

    Step 7: Write configuration.yml

    This is the heart of Authelia. Create /opt/authelia/config/configuration.yml:

    bash
    sudo tee /opt/authelia/config/configuration.yml > /dev/null <<'EOF'
    ###############################################################
    

    Authelia configuration #

    ###############################################################

    server: address: "tcp://0.0.0.0:9091"

    log: level: info format: text

    theme: dark

    totp: issuer: yourdomain.com period: 30 skew: 1

    webauthn: disable: false display_name: Authelia attestation_conveyance_preference: indirect user_verification: preferred timeout: 60s

    identity_validation: reset_password: jwt_lifespan: 5 minutes jwt_algorithm: HS256

    authentication_backend: password_reset: disable: false refresh_interval: 5 minutes file: path: /config/users_database.yml watch: true password: algorithm: argon2 argon2: variant: argon2id iterations: 3 memory: 65536 parallelism: 4

    access_control: default_policy: deny rules: # Public paths on auth portal itself - domain: auth.yourdomain.com policy: bypass

    # Healthcheck for monitoring - domain: "*.yourdomain.com" resources: - "^/api/health$" policy: bypass

    # Public marketing site, one factor only - domain: www.yourdomain.com policy: one_factor

    # Grafana — dev group, two factor - domain: grafana.yourdomain.com policy: two_factor subject: - "group:dev"

    # Portainer — admins only, two factor - domain: portainer.yourdomain.com policy: two_factor subject: - "group:admins"

    # Nextcloud — any logged in user, two factor - domain: nextcloud.yourdomain.com policy: two_factor

    # Catch-all: two factor required - domain: "*.yourdomain.com" policy: two_factor

    session: name: authelia_session same_site: lax inactivity: 15m expiration: 1h remember_me: 1M cookies: - domain: yourdomain.com authelia_url: https://auth.yourdomain.com default_redirection_url: https://www.yourdomain.com redis: host: redis port: 6379

    regulation: max_retries: 3 find_time: 2 minutes ban_time: 10 minutes

    storage: local: path: /config/db.sqlite3 # For production, prefer postgres: # postgres: # address: tcp://postgres:5432 # database: authelia # username: authelia # password_file: /secrets/postgres_password

    notifier: disable_startup_check: false smtp: address: "submissions://smtp.yourdomain.com:465" timeout: 5s username: [email protected] sender: "Authelia <[email protected]>" subject: "[Authelia] {title}" startup_check_address: [email protected] EOF sudo chmod 600 /opt/authelia/config/configuration.yml

    Rule Policies Explained

    PolicyMeaning
    bypassNo auth required. Use for health checks, public endpoints, the login page itself.
    one_factorUsername + password only. Fine for low-risk internal tools.
    two_factorUsername + password + TOTP/WebAuthn/Duo. Default for everything that matters.
    denyExplicitly block. The default_policy.
    Rules are evaluated top-to-bottom — put specific rules before catch-alls.

    Step 8: Start Authelia

    bash
    cd /opt/authelia
    sudo docker compose up -d

    Check logs:

    bash
    sudo docker compose logs -f authelia

    Expected output:

    text
    authelia  | time="2026-04-16T10:00:00Z" level=info msg="Authelia v4.38.x starting"
    authelia  | time="2026-04-16T10:00:00Z" level=info msg="Storage schema is up to date"
    authelia  | time="2026-04-16T10:00:00Z" level=info msg="Listening for non-TLS connections on '0.0.0.0:9091'"

    Verify health:

    bash
    curl http://127.0.0.1:9091/api/health

    Expected output:

    json
    {"status":"OK"}

    Step 9: Integrate with Nginx (Forward Auth)

    This is the most common deployment. Nginx asks Authelia "is this request allowed?" via a subrequest before letting it through to the upstream app.

    Authelia Portal Server Block

    Create /etc/nginx/sites-available/auth.yourdomain.com:

    nginx
    server {
        listen 443 ssl http2;
        server_name auth.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/auth.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/auth.yourdomain.com/privkey.pem;

    location / { proxy_pass http://127.0.0.1:9091; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }

    Reusable Forward-Auth Snippet

    Create /etc/nginx/snippets/authelia.conf:

    nginx
    # Internal Authelia location — used by auth_request
    location /internal/authelia/authz {
        internal;
        proxy_pass http://127.0.0.1:9091/api/authz/auth-request;

    proxy_pass_request_body off; proxy_set_header Content-Length "";

    proxy_set_header X-Original-Method $request_method; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-URI $request_uri; proxy_set_header X-Forwarded-Method $request_method; proxy_set_header X-Real-IP $remote_addr;

    proxy_set_header Content-Type ""; proxy_set_header Cookie $http_cookie;

    proxy_redirect http:// $scheme://; proxy_http_version 1.1; proxy_cache_bypass $cookie_session; proxy_no_cache $cookie_session; proxy_buffers 4 32k; client_body_buffer_size 128k;

    send_timeout 5m; proxy_read_timeout 240; proxy_send_timeout 240; proxy_connect_timeout 240; }

    Snippet to require Authelia auth on a location

    Include this inside any location { } you want to protect.

    set $target_url $scheme://$http_host$request_uri; auth_request /internal/authelia/authz; auth_request_set $user $upstream_http_remote_user; auth_request_set $groups $upstream_http_remote_groups; auth_request_set $name $upstream_http_remote_name; auth_request_set $email $upstream_http_remote_email;

    proxy_set_header Remote-User $user; proxy_set_header Remote-Groups $groups; proxy_set_header Remote-Name $name; proxy_set_header Remote-Email $email;

    Redirect to Authelia portal on 401

    error_page 401 =302 https://auth.yourdomain.com/?rd=$target_url;

    Protect an Upstream App (Grafana Example)

    /etc/nginx/sites-available/grafana.yourdomain.com:

    nginx
    server {
        listen 443 ssl http2;
        server_name grafana.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/grafana.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/grafana.yourdomain.com/privkey.pem;

    include snippets/authelia.conf;

    location / { include snippets/authelia.conf; proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }

    Enable and reload:

    bash
    sudo ln -s /etc/nginx/sites-available/auth.yourdomain.com /etc/nginx/sites-enabled/
    sudo ln -s /etc/nginx/sites-available/grafana.yourdomain.com /etc/nginx/sites-enabled/
    sudo nginx -t && sudo systemctl reload nginx

    Visit https://grafana.yourdomain.com. You will be redirected to https://auth.yourdomain.com to log in.

    Step 10: Alternative — Traefik Middleware

    If you use Traefik (How to Install Traefik on Ubuntu 24.04), define Authelia as a ForwardAuth middleware.

    /opt/traefik/dynamic/authelia.yml:

    yaml
    http:
      middlewares:
        authelia:
          forwardAuth:
            address: "http://authelia:9091/api/authz/forward-auth"
            trustForwardHeader: true
            authResponseHeaders:
              - "Remote-User"
              - "Remote-Groups"
              - "Remote-Name"
              - "Remote-Email"

    Attach the middleware to any router via Docker labels:

    yaml
    services:
      grafana:
        image: grafana/grafana:latest
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.grafana.rule=Host(grafana.yourdomain.com)"
          - "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
          - "traefik.http.routers.grafana.middlewares=authelia@file"
        networks:
          - proxy

    The same Authelia container serves both Nginx and Traefik deployments — pick whichever reverse proxy matches the rest of your stack.

    Step 11: Alternative — Caddy

    Caddy supports forward-auth via the caddy-security plugin or the built-in forward_auth directive (v2.6+):

    text
    grafana.yourdomain.com {
        forward_auth authelia:9091 {
            uri /api/authz/forward-auth
            copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
        }
        reverse_proxy grafana:3000
    }

    auth.yourdomain.com { reverse_proxy authelia:9091 }

    Caddy handles TLS automatically via Let's Encrypt — no Certbot needed.

    Step 12: First Login and TOTP Enrollment

  • Browse to https://grafana.yourdomain.com (or any protected app).
  • You are redirected to https://auth.yourdomain.com.
  • Enter username (e.g. alice) and password.
  • Because the rule requires two_factor, Authelia prompts for a second factor. First time, click Register device under "One-Time Password".
  • Authelia emails a secure enrollment link (that is why SMTP matters). Open the email, click the link.
  • Scan the QR code with Aegis, Google Authenticator, 1Password, Bitwarden, or any RFC 6238 TOTP app.
  • Enter the 6-digit code to confirm.
  • You are redirected back to Grafana, already logged in.
  • Next login: username + password + 6-digit code. Done.

    Step 13: WebAuthn Hardware Keys

    WebAuthn (FIDO2) gives you phishing-resistant 2FA with a YubiKey, Solokey, or platform authenticator (Touch ID, Windows Hello).

  • Log in to the Authelia portal at https://auth.yourdomain.com.
  • Click your username in the top right, then Two-Factor Authentication.
  • Under Security Keys, click Add.
  • Authelia sends an enrollment email — click the link.
  • Plug in your YubiKey and touch it when prompted (or use Touch ID on a MacBook).
  • Give it a nickname (e.g. "YubiKey 5C NFC").
  • You can register multiple keys per user — recommended for redundancy. On subsequent logins, WebAuthn appears as an option alongside TOTP.

    Step 14: OIDC Provider (Nextcloud Example)

    Some apps — Nextcloud, Gitea, Outline, Proxmox, Grafana Enterprise — speak OpenID Connect natively. Using OIDC instead of forward-auth gives you proper user provisioning and cleaner logout flows.

    Enable OIDC in Authelia

    Generate a client secret and its hash:

    bash
    docker run --rm authelia/authelia:4.38 \
      authelia crypto hash generate pbkdf2 --variant sha512 --password 'long-random-client-secret'

    Generate an HMAC and RSA key pair for signing tokens:

    bash
    openssl rand -hex 64 | sudo tee /opt/authelia/secrets/oidc_hmac_secret > /dev/null
    openssl genrsa -out /opt/authelia/secrets/oidc_private_key.pem 4096
    sudo chmod 600 /opt/authelia/secrets/oidc_*

    Add to configuration.yml:

    yaml
    identity_providers:
      oidc:
        hmac_secret: '{{ secret "/secrets/oidc_hmac_secret" }}'
        jwks:
          - key: {{ secret "/secrets/oidc_private_key.pem" | mindent 10 "|" | msquote }}
        cors:
          endpoints:
            - authorization
            - token
            - revocation
            - introspection
            - userinfo
          allowed_origins_from_client_redirect_uris: true
        clients:
          - client_id: nextcloud
            client_name: Nextcloud
            client_secret: '$pbkdf2-sha512$...PASTE_HASH_HERE...'
            public: false
            authorization_policy: two_factor
            redirect_uris:
              - https://nextcloud.yourdomain.com/apps/user_oidc/code
            scopes:
              - openid
              - profile
              - email
              - groups
            userinfo_signed_response_alg: none
            token_endpoint_auth_method: client_secret_basic

    Restart Authelia:

    bash
    cd /opt/authelia && sudo docker compose restart authelia

    Configure Nextcloud

  • Install the OpenID Connect user backend app from Nextcloud's app store.
  • Go to Settings -> Administration -> OpenID Connect.
  • Add a provider:
  • - Identifier: Authelia - Client ID: nextcloud - Client Secret: the plaintext secret (not the hash) - Discovery endpoint: https://auth.yourdomain.com/.well-known/openid-configuration - Scopes: openid profile email groups

    Click Login with Authelia on the Nextcloud login page. You are redirected through Authelia, complete 2FA, and land back in Nextcloud already authenticated.

    Other OIDC clients (Grafana, Gitea, Outline, Proxmox 8) follow the same pattern — add a client block in Authelia, register redirect URI, point the app at Authelia's discovery endpoint.

    Step 15: SMTP for Password Resets and Alerts

    Authelia sends mail for three reasons: 2FA enrollment links, password reset links, and security alerts (new device login, banned account). Without working SMTP, users cannot enroll TOTP or reset passwords.

    Option A: External SMTP (Mailgun, SendGrid, Postmark, Fastmail)

    The notifier.smtp block in configuration.yml already points at submissions://smtp.yourdomain.com:465. Replace with your provider:

    yaml
    notifier:
      smtp:
        address: "submissions://smtp.mailgun.org:465"
        username: [email protected]
        sender: "Authelia <[email protected]>"

    Store the password in /opt/authelia/secrets/smtp_password (already wired in Step 4).

    Option B: Local Mail Submission (Postfix relay)

    If you run Postfix on the host as a relay, point Authelia at it over the Docker bridge:

    yaml
    notifier:
      smtp:
        address: "smtp://host.docker.internal:25"
        disable_require_tls: true

    Add extra_hosts: ["host.docker.internal:host-gateway"] to the Authelia service in docker-compose.yml.

    Verify

    bash
    cd /opt/authelia && sudo docker compose logs authelia | grep -i smtp

    On startup Authelia sends a test email to startup_check_address. If you see msg="Notifier is operating correctly", SMTP is working.

    Step 16: Banned Users and Regulation

    The regulation block in configuration.yml automatically bans users who fail too many logins:

    yaml
    regulation:
      max_retries: 3
      find_time: 2 minutes
      ban_time: 10 minutes

    Three failures in two minutes = ten-minute ban. Bans are stored in SQLite/Postgres, survive restarts, and apply per user (not per IP — use fail2ban or your proxy for IP-level blocks).

    Manually Disable a User

    Edit /opt/authelia/config/users_database.yml:

    yaml
    bob:
      disabled: true

    File watch is enabled — the change takes effect within seconds, no restart needed. Set disabled: true when someone leaves; delete the entry entirely once you are sure.

    Manually Clear a Ban

    bash
    sudo docker exec -it authelia \
      sqlite3 /config/db.sqlite3 \
      "DELETE FROM authentication_logs WHERE username='alice';"
    sudo docker compose restart authelia

    Backups

    Authelia's state lives in three places:

  • /opt/authelia/config/db.sqlite3 (or your Postgres database) — TOTP secrets, WebAuthn credentials, identity verification tokens, audit log.
  • /opt/authelia/config/users_database.yml — users and password hashes.
  • /opt/authelia/config/configuration.yml + /opt/authelia/secrets/ — config and secrets. Without the secrets, existing sessions and encrypted storage are unreadable.
  • Simple nightly backup:

    bash
    sudo tee /etc/cron.daily/authelia-backup > /dev/null <<'EOF'
    #!/bin/bash
    DEST=/var/backups/authelia
    mkdir -p "$DEST"
    STAMP=$(date +%Y%m%d)
    tar --exclude='./redis' -czf "$DEST/authelia-$STAMP.tar.gz" -C /opt/authelia .
    find "$DEST" -name 'authelia-*.tar.gz' -mtime +30 -delete
    EOF
    sudo chmod +x /etc/cron.daily/authelia-backup

    Ship the archive offsite (restic, rclone, Backblaze B2) — the secrets file is small but non-regenerable for existing 2FA enrollments.

    Troubleshooting

    ProblemCauseSolution
    Infinite redirect loop to auth.yourdomain.comCookie domain mismatchEnsure session.cookies[0].domain matches your actual root domain. auth.yourdomain.com must be a subdomain of it, not the domain itself.
    401 Unauthorized from /api/authz/auth-request even after loginCookie not forwardedIn Nginx, make sure proxy_set_header Cookie $http_cookie is present in the internal location. Check for missing proxy_http_version 1.1.
    Authelia exits immediately with configuration key not expectedConfig schema drift between Authelia versionsCheck release notes at github.com/authelia/authelia/releases. v4.38 introduced several renames from v4.37.
    unable to connect to redisRedis container name or network wrongService name redis must match session.redis.host. Both containers must share the internal network.
    TOTP enrollment email never arrivesSMTP misconfigureddocker compose logs authelia \</td><td>grep -i smtp<code>. Check </code>submissions://<code> vs </code>smtp://, port 465 vs 587, and that the password file is readable.
    yaml: unmarshal errors on startupYAML indentation/typosdocker run --rm -v /opt/authelia/config:/config authelia/authelia:4.38 authelia validate-config --config /config/configuration.yml
    WebAuthn enrollment says "origin not allowed"Wrong server.address or webauthn.display_nameWebAuthn origin must exactly match the URL the browser visits (https://auth.yourdomain.com).
    New device alert emails on every loginsession.remember_me too short or session cookies being wipedIncrease remember_me or check your reverse proxy is not stripping the cookie.

    Validate Configuration Before Restarting

    bash
    sudo docker run --rm \
      -v /opt/authelia/config:/config \
      -v /opt/authelia/secrets:/secrets:ro \
      authelia/authelia:4.38 \
      authelia validate-config --config /config/configuration.yml

    Tail Logs Live

    bash
    cd /opt/authelia && sudo docker compose logs -f authelia

    Set log.level: debug in configuration.yml temporarily when chasing a specific issue — auth-request debugging is much noisier and more useful at debug level.

    FAQ

    Can Authelia replace Keycloak?

    For most self-hosted stacks, yes. Authelia covers OIDC (enough for Nextcloud, Gitea, Grafana, Outline, Proxmox) and forward-auth (for everything else). It does not implement SAML — if you have an app that only speaks SAML, Keycloak or Authentik is a better fit. If all your apps speak OIDC or live behind a reverse proxy, Authelia is lighter, faster, and simpler to operate.

    File backend or LDAP?

    Use the file backend (YAML with Argon2 hashes) for 1-50 users. It is the fastest to set up, has no external dependencies, and the users file is version-controllable. Move to LDAP (ideally lldap — a lightweight LDAP server purpose-built for this role) when you need a user database shared across multiple apps beyond Authelia, self-service password changes via LDAP clients, or group management from a UI.

    How do I add a new protected app?

    Three steps: (1) add a rule to access_control in configuration.yml, (2) create a reverse-proxy server block that includes the Authelia snippet, (3) reload both services. No Authelia restart is required if you only edit users_database.yml (file watch is on) — config changes require docker compose restart authelia.

    What happens if Authelia goes down?

    Every protected app returns 401 and redirects to the (also-down) portal. Run Authelia with restart: unless-stopped (already in our Compose file), set up an uptime monitor against https://auth.yourdomain.com/api/health, and for truly critical paths leave policy: bypass for your monitoring tool's health check endpoints so outside monitoring still works. For HA, run two Authelia containers behind a load balancer with a shared Redis and Postgres.

    Can I enforce a security key (WebAuthn) as the only 2FA method?

    Not today at the policy level — Authelia lets the user pick between enrolled methods (TOTP, WebAuthn, Duo). You can disable TOTP globally by setting totp.disable: true, forcing WebAuthn-only. The upside: phishing resistance. The downside: every user must own a hardware key or use a platform authenticator.

    How does this interact with Cloudflare Access or Tailscale?

    Authelia is application-layer auth; Cloudflare Access and Tailscale are network-layer. They stack. Running Cloudflare Access in front of Authelia gives you a DDoS shield and a first gate before a request even reaches your VPS. Tailscale gives you WireGuard network access to your apps, with Authelia handling per-user authorization once the request lands. For most single-admin VPS deployments, Authelia alone is sufficient.

    Next Steps

    • Add Authelia to all your existing apps — walk through your Nginx or Traefik config, wrap each server block with the forward-auth snippet, and rebuild your access_control rules top to bottom.
    • Move to Postgres for persistence — SQLite is fine for a few users, but Postgres scales better for OIDC deployments with many clients and audit logs. See the commented postgres: block in configuration.yml.
    • Deploy lldap for shared user directory — lldap gives you a clean web UI for user and group management that Authelia, Nextcloud, Jellyfin, and Vaultwarden can all share.
    • Set up uptime monitoring — point Uptime Kuma at https://auth.yourdomain.com/api/health and alert on 2xx failures.
    • Harden the VPS — review our Ubuntu 24.04 security hardening guide and make sure SSH keys, UFW, and unattended-upgrades are all in place.
    • Read the upstream docs — the Authelia documentation has deep dives on every config key, and the integration guides cover dozens of apps.

    Run Authelia Where It Belongs
    >
    Self-hosted SSO only works if the box it lives on is reliable. Our CloudCore Professional plan gives you 6 vCPU, 12 GB RAM, 100 GB NVMe, and unmetered bandwidth for EUR 19.99/month — enough for Authelia, Redis, Postgres, and a full stack of protected apps.
    >
    - 99.9% uptime SLA
    - European and North American data centers
    - Full root access — install anything
    - Snapshots and backups included
    >
    Deploy Your VPS Now and put SSO in front of everything you host.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket