ADR-075: Keycloak Horizontal Scaling via JGroups DNS_PING¶
Category: architecture Provenance: human
Decision¶
The Operator MUST automatically configure JGroups DNS_PING discovery for all managed Keycloak instances, enabling transparent horizontal scaling by simply increasing spec.replicas.
Implementation details: 1. Create a headless Service (clusterIP: None) for each Keycloak instance with port 7800 (JGroups TCP). 2. Set KC_CACHE_STACK=kubernetes environment variable to use TCP-based discovery instead of UDP multicast. 3. Set JAVA_OPTS_APPEND=-Djgroups.dns.query=<headless-service>.<namespace>.svc.cluster.local for DNS-based peer discovery. 4. Expose port 7800 in the Deployment container spec for JGroups communication.
This supersedes ADR-074 which recommended external Keycloak for HA scenarios.
Rationale¶
Keycloak requires JGroups/Infinispan clustering to share sessions and distributed caches across replicas. Without proper discovery, multiple replicas operate as isolated instances causing split-brain scenarios.
DNS_PING via headless Services is the Kubernetes-native approach: - No special RBAC permissions needed (unlike KUBE_PING which requires pod listing) - Works across all Kubernetes distributions - Simple configuration via environment variables - Automatic peer discovery through DNS A-records
The kubernetes cache stack (introduced in Keycloak 21) provides a pre-configured JGroups setup optimized for Kubernetes using TCP transport and DNS_PING discovery.
Agent Instructions¶
When users ask about scaling Keycloak, explain that horizontal scaling is now supported out-of-the-box. Users simply need to set spec.replicas to the desired count in the Keycloak CR. The operator automatically configures JGroups clustering via DNS_PING discovery.
For verification, users can check pod logs for ISPN000094: Received new cluster view showing the expected member count.
Note: This requires Keycloak 25.0.0+ (already a project requirement) and works best with the CNPG database backend for shared persistence.
Rejected Alternatives¶
KUBE_PING discovery¶
Requires additional RBAC permissions (pods/list, pods/get) in the Keycloak namespace. DNS_PING achieves the same result using standard Kubernetes DNS without extra permissions, aligning with our least-privilege security model.
UDP Multicast (default JGroups)¶
Most Kubernetes CNI implementations block or don't support multicast traffic. TCP with DNS_PING is universally supported across all Kubernetes environments.
External configuration only¶
Forces users to manually configure clustering, violating our GitOps-first design principle. Automatic configuration provides a better out-of-box experience while still allowing overrides.