Configure Keycloak high availability clustering for production with load balancing and failover

Advanced 90 min Apr 20, 2026 47 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up a production-ready Keycloak cluster with PostgreSQL backend, HAProxy load balancing, and automatic failover to ensure identity services remain available during node failures and high traffic.

Prerequisites

  • At least 7 servers (3 Keycloak, 2 PostgreSQL, 2 HAProxy)
  • Static IP addresses for all nodes
  • SSL certificates for HTTPS
  • Basic understanding of PostgreSQL administration
  • Root access on all servers

What this solves

Single Keycloak instances create identity bottlenecks and fail during peak authentication loads or server outages. This tutorial builds a production-ready Keycloak cluster with shared PostgreSQL storage, HAProxy load balancing, and automatic failover to handle thousands of concurrent users across multiple nodes.

Prerequisites and cluster planning

Infrastructure requirements

Plan your cluster topology before installation. You need at least 3 servers for a minimal production setup.

ComponentMinimum specsQuantityPurpose
HAProxy nodes2 CPU, 4GB RAM2Load balancing with redundancy
Keycloak nodes4 CPU, 8GB RAM3+Identity provider cluster
PostgreSQL nodes4 CPU, 16GB RAM2Database cluster with replication

Network configuration

Configure static IP addresses and hostname resolution across all cluster nodes.

# Add to all cluster nodes
203.0.113.10 haproxy1.example.com haproxy1
203.0.113.11 haproxy2.example.com haproxy2
203.0.113.20 keycloak1.example.com keycloak1
203.0.113.21 keycloak2.example.com keycloak2
203.0.113.22 keycloak3.example.com keycloak3
203.0.113.30 postgres1.example.com postgres1
203.0.113.31 postgres2.example.com postgres2

Firewall rules for cluster communication

Open required ports between cluster nodes for internal communication.

sudo ufw allow from 203.0.113.0/24 to any port 8080 comment "Keycloak HTTP"
sudo ufw allow from 203.0.113.0/24 to any port 7800 comment "Keycloak clustering"
sudo ufw allow from 203.0.113.0/24 to any port 5432 comment "PostgreSQL"
sudo ufw allow from 203.0.113.0/24 to any port 8080 comment "HAProxy stats"
sudo ufw reload
sudo firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=203.0.113.0/24 port protocol=tcp port=8080 accept"
sudo firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=203.0.113.0/24 port protocol=tcp port=7800 accept"
sudo firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=203.0.113.0/24 port protocol=tcp port=5432 accept"
sudo firewall-cmd --reload

Configure PostgreSQL database cluster

Install PostgreSQL on primary node

Set up the primary PostgreSQL instance that will replicate to secondary nodes.

sudo apt update
sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable --now postgresql
sudo dnf install -y postgresql-server postgresql-contrib
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql

Create Keycloak database and user

Set up the dedicated database and credentials for Keycloak cluster access.

sudo -u postgres psql
CREATE DATABASE keycloak;
CREATE USER keycloak_user WITH ENCRYPTED PASSWORD 'kc_cluster_pass_2024!';
GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak_user;
GRANT ALL ON SCHEMA public TO keycloak_user;
\q

Configure PostgreSQL for clustering

Enable streaming replication and configure connection limits for the Keycloak cluster.

# Connection settings
max_connections = 200
shared_buffers = 256MB
effective_cache_size = 1GB

Replication settings

wal_level = replica max_wal_senders = 3 max_replication_slots = 3 archive_mode = on archive_command = 'cp %p /var/lib/postgresql/16/main/archive/%f'

Network settings

listen_addresses = '*' port = 5432

Configure client authentication

Set up access rules for Keycloak nodes and replication users.

# Local connections
local   all             postgres                                peer
local   all             all                                     peer

Keycloak cluster connections

host keycloak keycloak_user 203.0.113.0/24 scram-sha-256

Replication connections

host replication replicator 203.0.113.31/32 scram-sha-256

Local network

host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256

Create replication user and restart

Add the replication user and apply configuration changes.

sudo -u postgres createuser --replication --pwprompt replicator
sudo systemctl restart postgresql
sudo systemctl status postgresql

Set up Keycloak cluster nodes

Install Java and download Keycloak

Install the required Java runtime and download Keycloak on all cluster nodes.

sudo apt update
sudo apt install -y openjdk-21-jdk wget
sudo useradd -r -s /bin/false keycloak
sudo mkdir -p /opt/keycloak
sudo dnf install -y java-21-openjdk-devel wget
sudo useradd -r -s /bin/false keycloak
sudo mkdir -p /opt/keycloak

Download and extract Keycloak

Get the latest Keycloak release and set up the directory structure.

cd /tmp
wget https://github.com/keycloak/keycloak/releases/download/22.0.5/keycloak-22.0.5.tar.gz
sudo tar -xzf keycloak-22.0.5.tar.gz -C /opt/keycloak --strip-components=1
sudo chown -R keycloak:keycloak /opt/keycloak
sudo chmod +x /opt/keycloak/bin/kc.sh

Configure database connection

Set up PostgreSQL connection parameters for the cluster database.

# Database configuration
db=postgres
db-url=jdbc:postgresql://postgres1.example.com:5432/keycloak
db-username=keycloak_user
db-password=kc_cluster_pass_2024!

Cluster configuration

cache=ispn cache-stack=tcp

HTTP configuration

http-host=0.0.0.0 http-port=8080

Hostname configuration

hostname=keycloak.example.com hostname-strict=false hostname-strict-https=false

Proxy configuration

proxy=edge

Health and metrics

health-enabled=true metrics-enabled=true

Configure cluster discovery

Set up JGroups configuration for automatic node discovery and communication.




    
        
            
            
            
            
        
    

    
        
        
        
        
        
        
        
        
        
        
    

Build optimized Keycloak

Build Keycloak with database and cluster optimizations for production.

sudo -u keycloak /opt/keycloak/bin/kc.sh build --db=postgres --cache=ispn

Create systemd service

Set up systemd service for automatic startup and process management.

[Unit]
Description=Keycloak Identity Provider
After=network.target

[Service]
Type=notify
User=keycloak
Group=keycloak
ExecStart=/opt/keycloak/bin/kc.sh start
TimeoutStartSec=600
TimeoutStopSec=30
RestartSec=5
Restart=always
StandardOutput=journal
StandardError=journal
SyslogIdentifier=keycloak

Environment="JAVA_OPTS=-Xms2g -Xmx4g -XX:MetaspaceSize=256m"
Environment="KEYCLOAK_ADMIN=admin"
Environment="KEYCLOAK_ADMIN_PASSWORD=admin_cluster_2024!"

[Install]
WantedBy=multi-user.target

Start Keycloak cluster nodes

Enable and start Keycloak on each cluster node in sequence.

sudo systemctl daemon-reload
sudo systemctl enable keycloak
sudo systemctl start keycloak
sudo systemctl status keycloak

Implement load balancing with HAProxy

Install HAProxy

Install HAProxy on both load balancer nodes for redundancy.

sudo apt update
sudo apt install -y haproxy keepalived
sudo dnf install -y haproxy keepalived

Configure HAProxy for Keycloak cluster

Set up load balancing with health checks and session affinity for Keycloak nodes.

global
    daemon
    maxconn 4096
    log stdout local0
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    option httplog
    option dontlognull
    option redispatch
    retries 3
    maxconn 2000

frontend keycloak_frontend
    bind *:80
    bind *:443 ssl crt /etc/ssl/certs/keycloak.pem
    redirect scheme https if !{ ssl_fc }
    
    # Security headers
    http-response set-header X-Frame-Options DENY
    http-response set-header X-Content-Type-Options nosniff
    http-response set-header X-XSS-Protection "1; mode=block"
    http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
    
    default_backend keycloak_backend

backend keycloak_backend
    balance roundrobin
    option httpchk GET /health/ready
    http-check expect status 200
    
    # Session affinity using cookies
    cookie KEYCLOAK_SERVER insert indirect nocache
    
    server keycloak1 keycloak1.example.com:8080 check cookie kc1 maxconn 500
    server keycloak2 keycloak2.example.com:8080 check cookie kc2 maxconn 500
    server keycloak3 keycloak3.example.com:8080 check cookie kc3 maxconn 500

listen stats
    bind *:8080
    stats enable
    stats uri /stats
    stats refresh 30s
    stats admin if TRUE

Configure keepalived for HAProxy failover

Set up virtual IP failover between HAProxy nodes using VRRP.

# Primary HAProxy node (haproxy1)
vrrp_script chk_haproxy {
    script "/bin/curl -f http://localhost:8080/stats || exit 1"
    interval 2
    weight -2
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 110
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass kc_vrrp_2024
    }
    virtual_ipaddress {
        203.0.113.100/24
    }
    track_script {
        chk_haproxy
    }
}
Note: On the secondary HAProxy node (haproxy2), change state to BACKUP and priority to 100.

Start load balancing services

Enable and start HAProxy and keepalived on both load balancer nodes.

sudo systemctl enable --now haproxy
sudo systemctl enable --now keepalived
sudo systemctl status haproxy
sudo systemctl status keepalived

Configure session replication and monitoring

Verify cluster formation

Check that Keycloak nodes have discovered each other and formed a cluster.

sudo journalctl -u keycloak -f | grep -i cluster
curl -s http://keycloak1.example.com:8080/health/ready
curl -s http://keycloak2.example.com:8080/health/ready
curl -s http://keycloak3.example.com:8080/health/ready

Test session replication

Verify that user sessions are shared across cluster nodes.

# Access admin console through load balancer
curl -I http://203.0.113.100/admin/

Check session distribution

curl -s http://keycloak1.example.com:8080/admin/realms/master/sessions

Configure cluster monitoring

Set up monitoring endpoints for cluster health and performance metrics. This integrates with existing HAProxy and Consul monitoring setups.

# Add monitoring configuration
metrics-enabled=true
health-enabled=true

JVM metrics

jvm-metrics-enabled=true

Cache metrics

cache-metrics-enabled=true

Test failover scenarios

Test Keycloak node failover

Simulate node failures to verify automatic failover and session continuity.

# Stop one Keycloak node
sudo systemctl stop keycloak

Test application access continues

curl -I http://203.0.113.100/admin/

Check HAProxy stats

curl -s http://203.0.113.100:8080/stats

Test HAProxy failover

Test virtual IP failover between HAProxy nodes.

# Stop primary HAProxy
sudo systemctl stop haproxy

Verify VIP moves to backup

ping 203.0.113.100

Test continued access

curl -I http://203.0.113.100/admin/

Test database failover

Verify application resilience during database maintenance windows.

# Promote PostgreSQL replica (on postgres2)
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main/

Update Keycloak database configuration

Restart Keycloak nodes with new primary database

Verify your setup

# Check all services are running
sudo systemctl status postgresql haproxy keepalived keycloak

Verify cluster health

curl -s http://203.0.113.100/health/ready curl -s http://203.0.113.100/health/live

Check HAProxy statistics

curl -s http://203.0.113.100:8080/stats

Verify admin console access

curl -I http://203.0.113.100/admin/

Check cluster member communication

sudo ss -tulpn | grep :7800

Common issues

SymptomCauseFix
Nodes not joining clusterFirewall blocking port 7800Open clustering ports: sudo ufw allow 7800
Database connection errorsPostgreSQL authentication failureCheck pg_hba.conf and restart PostgreSQL
Sessions not replicatingCache configuration mismatchVerify cache-ispn.xml is identical on all nodes
HAProxy health checks failingKeycloak health endpoint not respondingCheck health-enabled=true in keycloak.conf
VIP failover not workingKeepalived authentication mismatchVerify matching auth_pass in keepalived.conf
High memory usageJVM heap size misconfiguredTune JAVA_OPTS: -Xms4g -Xmx8g based on available RAM

Next steps

Running this in production?

Want this handled for you? Running Keycloak at scale adds complexity: capacity planning, security updates, performance tuning, and 24/7 incident response. See how we run infrastructure like this for European teams who need enterprise-grade identity services.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle infrastructure security hardening for businesses that depend on uptime. From initial setup to ongoing operations.