Episode 24 built HA with manual docker-compose; now it's time for orchestration. This episode deploys Authelia to Kubernetes using the official Helm chart, ConfigMap and Secret, Redis and PostgreSQL, Ingress with forward auth, up to HPA and network policies.

In episode 24 you built high availability with docker-compose: two instances, Redis, PostgreSQL, and a load balancer. That pattern works, but once the cluster grows, managing every container by hand becomes tedious and error-prone. Episode 25 brings the same pattern to Kubernetes: the official Authelia Helm chart.
In Kubernetes, a single values.yaml file replaces ten docker-compose files. Rolling updates, automatic restarts, scaling, and self-healing are handled by the platform. Authelia becomes a single Deployment that can be duplicated at will as long as Redis and the database remain shared components — exactly the same principle as episode 24.
Authelia maintains the official chart in the repo https://charts.authelia.com. Add it to Helm, then update the index:
helm repo add authelia https://charts.authelia.com
helm repo updateAfter that, the chart can be installed with helm install authelia authelia/authelia -f values.yaml. To see all supported value options, helm show values authelia/authelia displays the chart's very long default values.yaml — from image, configMap, secret, service, up to ingress.
The set of resources created by the chart can be inspected with helm template authelia authelia/authelia -f values.yaml or explored via kubectl get all -n authelia. In essence the chart produces:
| Resource | Role |
|---|---|
| Deployment | Authelia instance with liveness and readiness probes |
| Service | Stable access point inside the cluster (port 80 to 9091) |
| ConfigMap | The configuration.yaml file mounted to /config |
| Secret | Secret values like the session secret, JWT, and storage encryption key |
| Ingress / IngressRoute | Entry from outside the cluster, can use forward auth annotations |
Authelia's configuration is split in two: the parts that may be public (ConfigMap) and the parts that are secret (Secret). This is the same separation of concerns implementation as the secrets discussion in episodes 4 and 23.
The official chart mirrors Authelia's configuration structure under the configMap key. You don't write a separate configuration.yml file — everything is assembled via values.yaml. A minimal example for production deployment with file backend, PostgreSQL, and Redis:
replicaCount: 2
configMap:
key: configuration.yaml
log:
level: info
format: json
server:
address: 'tcp://0.0.0.0:9091'
authentication_backend:
file:
enabled: true
path: /config/users_database.yml
access_control:
default_policy: deny
rules:
- domain: app.example.com
policy: two_factor
storage:
postgres:
address: tcp://authelia-postgres:5432
database: authelia
username: authelia
password: ''
session:
redis:
host: authelia-redis-master
port: 6379
database_index: 0
notifier:
filesystem:
enabled: true
filename: /config/notification.txtNote the password: '' on storage and the session secret not yet filled — secret values like these should never be written directly in values.yaml if that file enters version control. Put them in a Secret.
The chart separates secrets into its own secret section. Two common approaches: filling via chart values, or referencing an existing Secret (existingSecret). The second approach is recommended because the values never touch values.yaml:
secret:
existingSecret: authelia-secretsThen create the Secret separately:
kubectl create secret generic authelia-secrets \
--namespace authelia \
--from-literal=sessionSecret='<64-random-characters>' \
--from-literal=jwtSecret='<64-random-characters>' \
--from-literal=storageEncryptionKey='<20-random-characters>'The secret keys stored in the Secret are: sessionSecret to encrypt sessions, jwtSecret to sign identity validation tokens, and storageEncryptionKey to encrypt MFA data in the database. Generate them all with openssl rand -base64 64 | head -c 64.
Tip
For more serious production, don't put raw secrets in a plain Secret. Use Sealed Secrets, the External Secrets Operator, or Vault — the principle you learned in the secret management series. A plain Secret is only base64 obfuscation, not encryption.
The Authelia chart doesn't manage Redis and PostgreSQL — they're separate components. For HA sessions, deploy Redis with Sentinel. Popular charts: bitnami/redis with architecture: replication, or the redis-ha chart which is specifically designed for scenarios like this.
helm repo add dandydev https://dandydev.github.io/helm-charts
helm install authelia-redis dandydev/redis-ha \
--namespace authelia \
--set sentinel.enabled=true \
--set auth.password='<redis-password>'PostgreSQL can use the bitnami/postgresql chart with replication, or — simpler and common in production — a managed database outside the cluster. An important note for Redis: the sentinel_name in Authelia's session configuration must exactly match the master name used by the Redis chart.
Access from outside comes in through an Ingress. There are two integration patterns: an external proxy (NGINX, Traefik, Caddy) in the cluster with forward auth, or Authelia itself as the application backend. For the NGINX Ingress Controller, attach the external-auth annotation to the Ingress of the application you want to protect:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/api/verify?rd=https%3A%2F%2Fauth.example.com%2F"
nginx.ingress.kubernetes.io/auth-signin: "https://auth.example.com/?rd=$request_uri"
nginx.ingress.kubernetes.io/auth-response-headers: "Remote-User,Remote-Groups,Remote-Name,Remote-Email"
spec:
ingressClassName: nginx
tls:
- hosts: [app.example.com]
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80The auth-url annotation points to Authelia's /api/verify endpoint — exactly the forward auth pattern you learned in episodes 13 to 16, only this time implemented as a Kubernetes annotation. TLS certificates can be managed with cert-manager; for Traefik, use the Middleware CRD with forwardAuth and IngressRoute.
Authelia already installs liveness and readiness probes to /api/health by default in the chart. What you need to watch for production:
resources.requests and resources.limits so Authelia doesn't starve nodes.An example HPA for Authelia:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: authelia
namespace: authelia
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: authelia
minReplicas: 2
maxReplicas: 6
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60When pods grow, remember the connection caps on Redis and the database. maximum_active_connections in the session and storage configuration limits how many connections one instance may open — adjust it to the replica count so the database pool doesn't run out.
Episode 25 brings Authelia to Kubernetes: adding the official Helm chart, understanding the resources produced, assembling configuration via configMap, separating secrets into a Secret or Sealed Secrets, deploying Redis and PostgreSQL, integrating an Ingress with forward auth annotations, and preparing probes, HPA, and network policies for production.
Key points:
configMap key./api/verify.Orchestration is done, but how do you know everything is running healthy? In episode 26 we dissect Monitoring & Logging: structured JSON logs, the Prometheus metrics endpoint, Grafana dashboards, alerting, and log aggregation with Loki. See you in episode 26!