A single Authelia instance is a single point of failure. This episode builds a high availability architecture: multiple instances behind a load balancer, Redis Sentinel for shared sessions, a shared database, proper health checks, up to a multi-instance docker-compose example.

Episode 23 covered the privacy side: Authelia doesn't send telemetry, data stays on your instance, and audit logs become the main activity trail. Now imagine that trail matters — then suddenly the Authelia server dies. All the applications it protects get locked out along with it. That's the problem episode 24 solves: high availability (HA).
Authelia is fundamentally stateless: the instance itself stores nothing. Sessions are stored in Redis, and persistent data (TOTP, WebAuthn, tokens) is stored in the storage backend. Consequently, Authelia can be scaled horizontally — the key is making Redis and the database shared components. This episode covers that architecture, from the concept, configuration, up to a ready-to-use docker-compose example.
Imagine an office building with one entrance and one security guard. That guard never gets sick? That door never breaks? Well, that's Authelia's position in a single-instance architecture: one point of failure. If Authelia goes down, every application gated by it becomes inaccessible too — including applications that are actually healthy.
The HA architecture splits that door into several gates sharing the same data. If one gate breaks, visitors enter through another gate without re-queuing. What makes this scenario possible is Authelia's stateless design: as long as all instances read sessions from the same Redis and data from the same database, user identity is still recognized.
| Component | Role | HA Solution |
|---|---|---|
| Authelia instance | Authentication & authorization server | Multiple instances behind a load balancer |
| Redis | Session storage | Redis Sentinel (master + replica, automatic failover) |
| Storage database | TOTP, WebAuthn, OIDC tokens | PostgreSQL with streaming replication |
| Load balancer | Traffic distribution | Health check to /api/health, automatic failover |
Authelia sessions are stored in Redis with the identity being a session ID carried by the cookie. Whichever instance receives the request can pull session data from the same Redis. If Redis isn't shared — for example each instance uses in-memory sessions — a user who logs in on instance A will be rejected by instance B, and a redirect loop appears.
For production, don't stop at a single Redis. Use Redis Sentinel: one master for writes, several replicas for reads, and Sentinel electing a new master automatically when the old master dies. Authelia supports this mode natively via the high_availability block under the Redis configuration:
session:
secret: '<session-secret-64-characters>'
name: authelia_session
expiration: 1h
inactivity: 5m
redis:
host: redis-sentinel-1
port: 26379
password: '<redis-password>'
database_index: 0
maximum_active_connections: 8
minimum_idle_connections: 0
high_availability:
sentinel_name: mymaster
nodes:
- host: redis-sentinel-2
port: 26379
- host: redis-sentinel-3
port: 26379Note two important things. First, the host above must be filled with the Sentinel address — not the regular Redis — because Authelia will ask Sentinel to find the currently active master. Second, sentinel_name isn't a hostname, but the master name defined in the Redis Sentinel configuration itself.
Important
Sessions don't sync by magic. When a Redis failover happens, sessions that haven't yet replicated to the new replica will be lost — users simply log in again. That's normal and far better than the entire service being down. Don't treat session loss as a system failure; treat it as a natural side effect of HA.
The Authelia storage backend stores TOTP secrets, WebAuthn credentials, identity verification tokens, up to OIDC consent data. If two instances each use their own SQLite, a user who registers MFA on instance A won't be visible on instance B. There's one solution: a shared database.
Use PostgreSQL with streaming replication: one primary for writes, one or more replicas for reads and failover. Authelia only writes to the primary, so put a connection pooler like PgBouncer in front of the primary, and manage primary switching with Patroni or a managed database service.
storage:
postgres:
address: tcp://postgres-primary:5432
database: authelia
username: authelia
password: '<storage-password>'
ssl:
mode: require
maximum_active_connections: 8
minimum_idle_connections: 0With this scheme, all instances read and write to the same dataset, and MFA registered on any instance is recognized across all instances.
The load balancer is the single gate that receives traffic and forwards it to healthy instances. Because sessions are stored in shared Redis, sticky sessions aren't required — requests can land on any instance and the user is still recognized. Enabling sticky sessions is fine to reduce a little round-tripping, but don't make it an architectural dependency; if an instance dies, the user's session must survive.
Health checks determine which instance is "healthy" enough to receive traffic. Authelia provides a simple endpoint at /api/health that returns an OK status — check it directly with curl -fsS https://auth.example.com/api/health:
curl -fsS https://auth.example.com/api/healthThe response is JSON: {"status":"OK"}. Important note: this endpoint only confirms the Authelia HTTP process is alive, not that Redis and the database are connected. For an extra layer of trust, add dependency checks at a separate layer — for example redis-cli ping and a SELECT 1 against the database.
Here's a docker-compose skeleton with two Authelia instances, a Redis master with one replica (simplified without full Sentinel for focus), and a shared PostgreSQL:
services:
authelia-1:
image: authelia/authelia:4.38
container_name: authelia-1
restart: unless-stopped
volumes:
- ./configuration.yml:/config/configuration.yml:ro
- ./users_database.yml:/config/users_database.yml:ro
networks: [auth]
depends_on:
redis-master:
condition: service_healthy
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:9091/api/health"]
interval: 30s
timeout: 5s
retries: 3
authelia-2:
image: authelia/authelia:4.38
container_name: authelia-2
restart: unless-stopped
volumes:
- ./configuration.yml:/config/configuration.yml:ro
- ./users_database.yml:/config/users_database.yml:ro
networks: [auth]
depends_on:
redis-master:
condition: service_healthy
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:9091/api/health"]
interval: 30s
timeout: 5s
retries: 3
redis-master:
image: redis:7-alpine
command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD}"]
networks: [auth]
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
redis-replica:
image: redis:7-alpine
command: ["redis-server", "--replicaof", "redis-master", "6379", "--appendonly", "yes"]
networks: [auth]
depends_on:
redis-master:
condition: service_healthy
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: authelia
POSTGRES_USER: authelia
POSTGRES_PASSWORD: ${STORAGE_PASSWORD}
networks: [auth]
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U authelia"]
interval: 10s
timeout: 5s
retries: 5
networks:
auth:
volumes:
redis-data:
pgdata:Both instances use the exact same configuration file. The only difference is the container name — necessary so they don't conflict on the Docker network. In front of these two instances stands a reverse proxy or load balancer that forwards traffic and checks their health.
Enable AOF (--appendonly yes) on Redis so valid sessions can recover after a restart. Bring the stack up with docker compose up -d. For more serious production use, replace the pattern above with full Sentinel or a managed Redis service, plus PostgreSQL with streaming replication.
Episode 24 gives you the foundation of Authelia HA: understanding why Authelia is stateless so it can be duplicated, sharing sessions via Redis Sentinel, sharing persistent data via PostgreSQL, applying proper health checks, and assembling everything into a single multi-instance docker-compose.
Key points:
/api/health health check must be used by the load balancer; add separate dependency checks.Managing many containers by hand can get messy — when the scale grows, the answer is orchestration. In episode 25 we move to Kubernetes Deployment: the official Authelia Helm chart, ConfigMap and Secret for configuration, Ingress with forward auth, up to HPA. See you in episode 25!