Learn Keycloak - Performance Tuning & Monitoring
Episode 28 of 31

Learn Keycloak - Performance Tuning & Monitoring

Optimizing Keycloak's performance through JVM, database pool, and cache tuning, then monitoring with Prometheus metrics, Grafana dashboards, JMX, and capacity planning for user growth.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 27 you built a fault-tolerant cluster. Episode 28 makes it fast and measurable: performance tuning so Keycloak serves many logins without ballooning, and monitoring so you know when to act before users complain. Tuning without metrics is guessing; metrics without tuning is watching.

JVM & Resource Optimization

Keycloak runs on the JVM, and memory is the first setting to pay attention to:

  • Heap size — set via the KC_HEAP variable. A heap that's too small triggers constant GC; too large wastes RAM and lengthens GC pauses.
  • Garbage collection — choose a GC matching the load profile; for services with login spikes, more frequent but short GCs are usually better than long pauses.
  • Thread pool — the worker count determines how many requests can be processed concurrently; adjust to the CPU cores.
  • Database pool — the connection pool must be enough to absorb spikes, but not exceed the database's capacity.
Setting heap and database pool
KC_HEAP=2g \
KC_DB_POOL_MIN_SIZE=5 \
KC_DB_POOL_MAX_SIZE=50 \
kc.sh start

KC_HEAP=2g sets the memory limit; KC_DB_POOL_MIN_SIZE and KC_DB_POOL_MAX_SIZE govern the number of database connections. For a small instance start with 2 GB of heap; for enterprise loads with thousands of concurrent logins, raise it step by step while observing metrics. Every added cluster node (episode 27) also adds demand on the database — adjust the pool on both sides.

As a rule of thumb: change one variable at a time, then observe its impact on response time and GC. Changing heap, pool, and cache all at once leaves you unable to tell which change actually mattered.

Caching

Most of Keycloak's speed comes from the Infinispan cache. The relevant caches:

CacheContentsEffect if too small
realmsrealm configurationslow logins, frequent config reloads
userscached user profilesmore user queries to the database
authorizationresources, policies, permissionsauthorization evaluation slows down
sessionsactive sessionsusers frequently logged out, sessions lost
offlineSessionsoffline sessionsoffline tokens often fail

The cache size must be enough to hold the active user population, but shouldn't waste memory. Cache invalidation is managed by Keycloak automatically when data changes; in a cluster, a change on one node is propagated via JGroups so the cache stays consistent. For very large loads, the session store can be moved to the database so memory doesn't fill up — a trade-off between speed and scale that you must decide based on measurement, not feelings.

Monitoring

Without observation, tuning has no direction. Enable Keycloak metrics:

Enabling the metrics endpoint
KC_METRICS_ENABLED=true kc.sh start --optimized

With KC_METRICS_ENABLED=true, Keycloak exposes Prometheus metrics at the /metrics endpoint. Prometheus then scrapes it periodically:

Prometheus scrape config
scrape_configs:
  - job_name: keycloak
    metrics_path: /metrics
    static_configs:
      - targets: ["sso.example.com:8080"]

The metric data is then visualized with Grafana dashboards — many ready-to-use Keycloak community dashboards exist. For internal JVM details, use JMX (for example via JConsole or exporting to Prometheus with a JMX exporter). Centralized logging with ELK or Loki helps correlate across nodes; APM (Application Performance Monitoring) gives request traces from the load balancer down to the database.

Once metrics are flowing, set up alerting: alarms for a dropping login rate, a rising error rate, and a falling cache hit ratio are the three triggers that most often save a team from an incident. Grafana can be connected to Alertmanager or other notifications, so responses don't depend on whoever happens to open the dashboard.

Key Metrics

Here are the metrics you should always watch:

MetricMeaningDanger Sign
Login throughputnumber of logins per secondDrops suddenly as load rises
Token generation ratetokens issued per secondSpikes without cause
Response timeslogin and token endpoint latencyKeeps rising = a bottleneck
Error ratespercentage of failed requests5xx increasing = disruption
Cache hit ratiosproportion of accesses served from cacheFalling = wrong cache sizing
DB connection pooldatabase pool usageExhausted = requests queueing
JVM memoryused heap and GC frequencyHeap approaching the limit

Besides Prometheus metrics, make use of the health endpoints already used in episode 27: /health/live for process liveness, /health/ready for readiness to serve, and /health/started for startup status. These health alarms are what inform the load balancer and your on-call team.

Capacity Planning

Monitoring answers "what's happening now"; capacity planning answers "what happens next month":

  • User load estimation — project the number of active users and logins per hour from growth trends.
  • Resource sizing — translate the load estimate into the number of nodes, heap, and database size.
  • Scalability testing — prove with real load that adding nodes truly adds capacity.
  • Growth projections — plan headroom: don't wait until metrics hit the limit to add resources.

A recommended capacity planning process:

  1. Record the current load baseline from the metrics you've installed.
  2. Project user growth from the business roadmap, not assumptions.
  3. Measure the impact of each scenario through load testing in the staging environment.
  4. Put a resource addition plan in place long before the limit is reached.
  5. Review the projections quarterly to keep them relevant.

Tip

Store a baseline from normal load. During an incident, comparing metrics against the baseline is far more informative than looking at absolute numbers — for example "cache hit ratio dropped from 98 percent to 80 percent" immediately points to a cache sizing problem.

Closing

Episode 28 equipped your cluster with speed and vision: JVM tuning via KC_HEAP, database pool and cache; Prometheus metrics via KC_METRICS_ENABLED=true; Grafana visualization; JMX observation and centralized logging; key metrics; and trend-based capacity planning.

Key takeaways:

  • Tuning needs metrics — measure first, change one variable, measure again.
  • The cache is the main source of speed — watch the hit ratio and the size of the realms, users, and sessions caches.
  • Health endpoints are the first communicationlive, ready, started signal the load balancer and alerting.
  • Capacity is planned, not reacted to — headroom must exist before metrics hit the limit.

In the next episode (episode 29), you'll protect all this hard work: backup, disaster recovery & upgrades — from database and configuration backups, RTO and RPO, to safe version upgrades.