This episode covers Authentik's high availability architecture: multiple server and worker replicas, shared PostgreSQL and Redis, outpost scaling, load balancers, session storage, health checks, failover, and an example multi-replica docker-compose.

In episode 22, you built observability. Now make sure the service itself isn't a single point of failure. An identity provider is critical infrastructure: if Authentik dies, all the applications depending on it lose login too. Episode 23 covers high availability — multiple replicas, a shared data layer, and a load balancer.
Think of it like an airport with one runway: as soon as the runway is disrupted, all flights stop. HA means adding a second and third runway, and keeping the schedule running even when one runway is closed.
An HA production Authentik architecture:
The key: Authentik instances are stateless. All state lives in PostgreSQL and Redis, so adding a replica is just a matter of adding a process, not copying data.
Because servers are stateless, any request can be served by any replica. One exception: the media folder (logos, favicons, and other uploaded files) must be shared between replicas — via a shared volume in Docker or distributed storage in Kubernetes — so assets don't appear missing depending on which replica serves.
Sessions in Authentik use signed cookies (JWT) validated against data in the database and Redis. Two implications:
The data layer is the hardest part to make HA, because there must still be one source of truth:
A rule that must not be violated: all Authentik replicas must point to the same PostgreSQL and Redis pair; splitting them per instance would break state.
Outposts are also scaled horizontally: multiple LDAP or proxy instances share the same outpost token and work in parallel behind their own load balancers. The status of each instance is visible in the UI; if one dies, the others keep serving because the configuration is fetched from the central server.
The load balancer distributes traffic across server replicas. A typical NGINX configuration:
upstream authentik {
server authentik-server-1:9000;
server authentik-server-2:9000;
}
server {
listen 443 ssl;
server_name auth.example.com;
ssl_certificate /etc/nginx/tls/fullchain.pem;
ssl_certificate_key /etc/nginx/tls/privkey.pem;
location / {
proxy_pass http://authentik;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Note the X-Forwarded-* headers: Authentik uses this information to determine the real client IP (important for reputation in episode 19) and the HTTPS scheme.
To determine which replicas are fit to receive requests, Authentik provides two endpoints:
/health/live/ — the process is running and responding./health/ready/ — ready to serve; verifies the database and Redis connections.Example of a quick check against the /health/ready/ endpoint:
curl -f http://localhost:9000/health/ready/Attach both to the container health checks and the load balancer probes. ready determines whether a replica may receive traffic; live only indicates the process hasn't died.
With the architecture above, failure flows become simple:
Important: test these scenarios regularly. A failover that's never been drilled often ends up as a failover that fails when actually needed.
Here's a compose skeleton with two server replicas, one worker, PostgreSQL, and Redis. Notice that every server uses the same database and Redis, and shares the media volume:
services:
postgres:
image: docker.io/library/postgres:16-alpine
environment:
POSTGRES_USER: authentik
POSTGRES_PASSWORD: ${AUTHENTIK_PG_PASSWORD}
POSTGRES_DB: authentik
volumes:
- pg-data:/var/lib/postgresql/data
redis:
image: docker.io/library/redis:7-alpine
command: --appendonly yes
volumes:
- redis-data:/data
server-1:
image: ghcr.io/goauthentik/server:2025.6
command: server
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_PG_PASSWORD}
AUTHENTIK_REDIS__HOST: redis
volumes:
- media:/media
server-2:
image: ghcr.io/goauthentik/server:2025.6
command: server
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_PG_PASSWORD}
AUTHENTIK_REDIS__HOST: redis
volumes:
- media:/media
worker:
image: ghcr.io/goauthentik/server:2025.6
command: worker
environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_PG_PASSWORD}
AUTHENTIK_REDIS__HOST: redis
volumes:
pg-data:
redis-data:
media:In a real deployment, the two replicas are usually on different hosts (or managed by an orchestrator like Kubernetes — covered in episode 24), and the load balancer sits in front of both servers' port 9000.
Warning
HA adds replicas, it doesn't replace backups. Still back up PostgreSQL regularly and test the recovery. Replicas protect against hardware failure; backups protect against human error and data corruption — two different problems.
Summary of episode 23:
/health/live/ and /health/ready/ health checks form the basis of load balancer and failover decisions.In episode 24, we take this pattern to a more managed scale: Kubernetes deployment with a Helm chart, orchestrator-managed replicas, and persistent storage. See you there!