Back to Blogs
#security#docker#devops#auth

Deploying Authelia Open-Source Authentication and Authorization Gateway on Ubuntu 24.04

Authelia is an open-source authentication and authorization gateway that adds SSO, two-factor...

4 min read
Cover for Deploying Authelia Open-Source Authentication and Authorization Gateway on Ubuntu 24.04

Authelia is an open-source authentication and authorization gateway that adds SSO, two-factor authentication, and policy-based access control in front of web applications, integrating natively with reverse proxies through forward-auth. This guide deploys Authelia using Docker Compose with Traefik handling automatic HTTPS, file-backed users, TOTP 2FA, and a sample whoami app protected by forward-auth. By the end, you'll have Authelia gating multiple subdomains with SSO and 2FA over HTTPS.


Set Up the Directory Structure

1. Create the project directory structure:

$ mkdir -p ~/authelia/{config,secrets,logs}
$ cd ~/authelia
Enter fullscreen mode Exit fullscreen mode

2. Create the environment file:

$ nano .env
Enter fullscreen mode Exit fullscreen mode
DOMAIN=example.com
LETSENCRYPT_EMAIL=admin@example.com
Enter fullscreen mode Exit fullscreen mode

DNS A records for auth.${DOMAIN}, app.${DOMAIN}, and traefik.${DOMAIN} must point at the server.


Generate Authelia Secrets

1. Own the secrets directory:

$ sudo chown 8000:8000 ./secrets
$ sudo chmod 0700 ./secrets
Enter fullscreen mode Exit fullscreen mode

2. Generate session, storage, and JWT secrets in one shot:

$ docker run --rm -u 8000:8000 -v ./secrets:/secrets docker.io/authelia/authelia:4.39 \
    sh -c "cd /secrets && authelia crypto rand --length 64 session_secret.txt storage_encryption_key.txt jwt_secret.txt"
Enter fullscreen mode Exit fullscreen mode

Write the Authelia Configuration

1. Create the configuration file:

$ nano config/configuration.yml
Enter fullscreen mode Exit fullscreen mode
server:
  address: 'tcp4://:9091'

log:
  level: 'info'
  file_path: '/var/log/authelia/authelia.log'
  keep_stdout: true

identity_validation:
  elevated_session:
    require_second_factor: true
  reset_password:
    jwt_lifespan: '5 minutes'
    jwt_secret: {{ secret "/secrets/jwt_secret.txt" | mindent 0 "|" | msquote }}

totp:
  disable: false
  issuer: 'example.com'
  period: 30
  skew: 1

authentication_backend:
  file:
    path: '/config/users.yml'
    password:
      algorithm: 'argon2'
      argon2:
        variant: 'argon2id'
        iterations: 3
        memory: 65535
        parallelism: 4
        key_length: 32
        salt_length: 16

access_control:
  default_policy: 'deny'
  rules:
    - domain: 'app.example.com'
      policy: 'two_factor'
    - domain: 'traefik.example.com'
      policy: 'one_factor'

session:
  name: 'authelia_session'
  secret: {{ secret "/secrets/session_secret.txt" | mindent 0 "|" | msquote }}
  cookies:
    - domain: 'example.com'
      authelia_url: 'https://auth.example.com'

regulation:
  max_retries: 4
  find_time: 120
  ban_time: 300

storage:
  encryption_key: {{ secret "/secrets/storage_encryption_key.txt" | mindent 0 "|" | msquote }}
  local:
    path: '/config/db.sqlite3'

notifier:
  disable_startup_check: false
  filesystem:
    filename: '/config/notification.txt'
Enter fullscreen mode Exit fullscreen mode

2. Generate an argon2 hash for the first user's password:

$ docker run --rm authelia/authelia:4.39 authelia crypto hash generate argon2 --password 'your-secure-password'
Enter fullscreen mode Exit fullscreen mode

3. Create the user database with that hash:

$ nano config/users.yml
Enter fullscreen mode Exit fullscreen mode
users:
  authuser:
    displayname: 'Auth User'
    password: '$argon2id$v=19$m=65536,t=3,p=4$BpLnfgDsc2WD8F2q$Zis.ixdg9s/UOJYrs56b5QEZFiZECu0qZVNsIYxBaNJ7ucIL.nlxVCT5tqh8KHG8X4tlwCFm5r6NTOZZ5qRFN/'
    email: 'authuser@example.com'
    groups:
      - 'admin'
Enter fullscreen mode Exit fullscreen mode

Deploy with Docker Compose

1. Create the Docker Compose manifest:

$ nano docker-compose.yaml
Enter fullscreen mode Exit fullscreen mode
services:
  traefik:
    image: traefik:v3.6
    container_name: traefik
    depends_on:
      - authelia
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.${DOMAIN}`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.middlewares=authelia@docker"
    restart: unless-stopped

  authelia:
    image: authelia/authelia:4.39
    container_name: authelia
    volumes:
      - "./secrets:/secrets:ro"
      - "./config:/config"
      - "./logs:/var/log/authelia"
    environment:
      TZ: "UTC"
      X_AUTHELIA_CONFIG_FILTERS: "template"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.authelia.rule=Host(`auth.${DOMAIN}`)"
      - "traefik.http.routers.authelia.entrypoints=websecure"
      - "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-Name,Remote-Email"
    restart: unless-stopped

  whoami:
    image: traefik/whoami
    container_name: whoami
    depends_on:
      - authelia
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`app.${DOMAIN}`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
      - "traefik.http.routers.whoami.middlewares=authelia@docker"
    restart: unless-stopped
Enter fullscreen mode Exit fullscreen mode

2. Start the services:

$ docker compose up -d
Enter fullscreen mode Exit fullscreen mode

3. Verify the services are running:

$ docker compose ps
$ docker compose logs
Enter fullscreen mode Exit fullscreen mode

Register TOTP and Test SSO

1. Open the login portal:

Navigate to https://auth.example.com and sign in as authuser.

2. Register a TOTP device:

Click Register device and capture the verification code from the filesystem notifier:

$ docker compose exec authelia cat /config/notification.txt
Enter fullscreen mode Exit fullscreen mode

Scan the QR code with an authenticator app and enter the 6-digit code.

3. Visit a protected app:

Open https://app.example.com — the request redirects through Authelia for password + TOTP, then the whoami container returns the injected identity headers.

4. Confirm SSO:

Open https://traefik.example.com from the same browser — no re-authentication required.


Next Steps

Authelia is gating multiple subdomains with SSO and 2FA. From here you can:

  • Protect more apps by adding traefik.http.routers.<svc>.middlewares=authelia@docker and a matching access_control rule
  • Switch the authentication backend to LDAP or OpenID Connect 1.0
  • Configure SMTP under notifier to deliver verification codes by email

For the full guide with additional tips, visit the original article on Vultr Docs.