Authelia is lightweight, but there are still points that can slow down: Redis, database connections, and the reverse proxy. This episode covers benchmarking, Redis and PostgreSQL tuning, keep-alive and static asset caching at the proxy, and capacity planning for real user scales.

Episode 27 made Authelia safe: data can be recovered at any time. Now you're adding users — dozens, hundreds, thousands. The next question arises: does this gate stay fast when everyone logs in at once? Episode 28 answers it: performance tuning.
Authelia is written in Go and is very lightweight — at idle it uses memory below a few dozen megabytes. That means the bottleneck is almost never in the Authelia binary itself. The points to watch are around it: connections to Redis, the database connection pool, and the reverse proxy in front. Good tuning is reducing work that doesn't actually need to be done.
The first rule you've learned in previous episodes: don't tune without a baseline. Before changing anything, build base numbers with a benchmark. Start from the lightest endpoint — the health endpoint — for example with hey -n 10000 -c 100 http://127.0.0.1:9091/api/health:
hey -n 10000 -c 100 http://127.0.0.1:9091/api/healthThen benchmark a more realistic flow — the verify endpoint with a session cookie and Remote-User header, as the reverse proxy does on every request:
hey -n 5000 -c 50 \
-H "Cookie: authelia_session=<cookie-value>" \
-H "Remote-User: arman" \
"https://auth.example.com/api/verify?rm=0"Note the requests per second numbers and p50/p99 latencies. Change one variable, measure again, compare. Every optimization in this episode is that cycle — measure, change, measure again.
Tip
For load tests that can enter CI, tools like hey, ab, or k6 can be run as repeatable benchmarks. Save the results in a document — when the scale grows, you need a reference to know whether performance dropped because of a configuration change or data growth.
Redis is the component most frequently touched by requests. Every verify and every session access hits it, so its connections and memory policy have direct impact.
Authelia manages its own Redis connection pool. The maximum_active_connections and minimum_idle_connections values determine how many parallel connections are kept:
session:
redis:
host: redis
port: 6379
database_index: 0
maximum_active_connections: 16
minimum_idle_connections: 4On the Redis server side, watch the memory policy. Authelia sessions use TTL, so volatile-lru eviction — only evicting keys that have a TTL — is the right choice; non-session keys you might store on the same instance won't be evicted:
maxmemory 1gb
maxmemory-policy volatile-lru
appendonly yes
appendfsync everysecappendonly yes with per-second fsync gives a balance between session durability (episode 27) and write performance. If sessions don't need to survive a restart, RDB snapshots alone are enough and faster. Verify the active policy with redis-cli CONFIG GET maxmemory-policy.
Authelia writes to the database only on changes — first login, MFA registration, OIDC tokens. That makes the database not a per-request critical path, except for one thing: wasteful connections. Each Authelia instance opens its own connection pool; with many replicas, total connections can pile up.
Set the per-instance pool as needed, then keep the total well below PostgreSQL's max_connections:
storage:
postgres:
address: tcp://postgres:5432
database: authelia
username: authelia
password: '<password>'
maximum_active_connections: 12
minimum_idle_connections: 2A rough formula: PostgreSQL max_connections minus maintenance connections, divided by the number of Authelia instances. For medium scale, a pool of 8-12 per instance is already plenty. PgBouncer in front of PostgreSQL solves the many instances, one database problem by sharing connections from a small pool.
Don't forget routine maintenance. Keep autovacuum enabled (on by default) so heavily written tables don't bloat, and monitor long-running queries via the monitoring from episode 26. Check connection health periodically with psql -U authelia -h postgres -d authelia -c "SELECT 1". Authelia's schema has its own indexes — don't create extra indexes without load data showing they're needed.
Because all traffic passes through the proxy, this is where the biggest wins come with little effort:
upstream authelia {
server authelia-1:9091;
server authelia-2:9091;
keepalive 32;
}
server {
listen 443 ssl;
server_name auth.example.com;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
location / {
proxy_pass http://authelia;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}ssl_session_cache makes the TLS handshake short for repeat connections — a real latency drop for returning users.Authelia is designed to be scaled horizontally — exactly what was built in episodes 24 and 25. When load rises, adding instances is easier than fiddling with configuration. But watch two limits that don't grow along with it:
For capacity planning, start from real numbers, not guesses: measure peak active sessions (Redis metrics or logs), compute authentication RPS from the authelia_authn_total metric in episode 26, then multiply by a rule of thumb: one modern Authelia instance easily handles hundreds of verification RPS. For thousands of users with simultaneous peaks, two to four instances are almost always enough. Give 2-3 times headroom over the peak need, and use HPA (episode 25) so scaling is automatic during surges.
Episode 28 trains you to tune performance with data: building a baseline with the hey benchmark on the health and verify endpoints, setting the Redis connection pool and the volatile-lru eviction policy, keeping the database pool small with PgBouncer as an umbrella, optimizing the reverse proxy via keep-alive, static asset caching, and TLS session resumption, and doing number-based capacity planning.
Key points:
maxmemory-policy volatile-lru so TTL sessions are evicted correctly.Even with the best configuration, a day will come when something breaks. In episode 29 we dissect Troubleshooting & Debugging: 503 from the proxy, redirect loops, session problems, TOTP failures, up to reading logs and validating the configuration. See you in episode 29!