How Much RAM and CPU Does Keycloak Need in Production?

Guilliano Molaire Guilliano Molaire Updated July 28, 2026 12 min read

Last updated: November 2026

Keycloak is a moderate resource consumer, not a heavy one. A single production pod commonly runs comfortably with roughly 1-2 vCPU and 1-1.5 GB of RAM for the container with a bounded JVM heap — but those figures can shift significantly based on your workload. The real driver is your login and token-refresh rate plus your active concurrent session count, not your total registered user count. Size your deployment from actual load tests, run at least two replicas for high availability, and give your database its own dedicated resources. As a load-tested rule of thumb from Keycloak’s own guidance, allocate roughly 1 vCPU for every 15 password logins per second and about 1,250 MB of RAM per pod as a baseline, then adjust from there.

Getting resource planning wrong in either direction is costly. Under-provisioning leads to slow logins, rejected requests, and authentication outages that cascade across every application that trusts Keycloak. Over-provisioning wastes budget and creates a false sense of safety when the real bottleneck is the database or network. This guide explains the mechanisms that drive resource consumption so you can make an informed decision rather than guessing.

What actually drives CPU usage

CPU consumption in Keycloak is dominated by a small number of operations, and understanding which ones matter most changes how you think about sizing.

Password hashing is the largest CPU factor at login

When a user authenticates with a username and password, Keycloak must verify the stored credential. Modern credential storage uses intentionally slow hashing algorithms — PBKDF2 by default in older Keycloak versions, with Argon2 available as an alternative in recent releases. These algorithms are designed to be expensive to compute so that brute-force attacks against a stolen credential database are impractical.

The iteration count (or cost factor for Argon2) is the control knob. Keycloak’s defaults have increased over major releases to keep pace with hardware improvements. A higher iteration count is more secure but consumes more CPU per login event. On a constrained node, a sudden burst of real user logins — a Monday morning spike or a post-maintenance flood — will show up immediately as CPU saturation because each login triggers a full hash computation.

This has a practical implication: your peak login rate, not your total user count, drives CPU demand. An organization with 500,000 registered users who log in gradually throughout the day may need less CPU than one with 10,000 users who all authenticate within a 15-minute window.

Token signing

Every issued access token and ID token is cryptographically signed. For RS256 and ES256 — common choices in production — signing requires an asymmetric key operation. This is cheaper per operation than password hashing, but it happens on every token issuance and on every call that goes through token introspection. High-throughput APIs that validate tokens on each request amplify this load indirectly (though token validation should happen in the application layer, not by calling Keycloak on every request).

Realm and client evaluation

Each authentication flow evaluates realm policies, client configurations, and potentially custom authenticators. Deployments with many realms, complex authorization policies, or heavy use of custom authentication scripts carry a higher per-request CPU cost than simple single-realm deployments.

What actually drives RAM usage

RAM usage in Keycloak (running on Quarkus since Keycloak 17+) is primarily determined by two things: the JVM heap and the Infinispan in-process caches.

JVM heap

Keycloak runs on the JVM. The heap holds live objects: active request state, loaded configuration, deserialized tokens, and short-lived processing buffers. Heap pressure grows with concurrent request volume and, to a lesser extent, with the number of realms and clients loaded into memory.

You control the maximum heap with -Xmx (or XX:MaxRAMPercentage in containerized environments). Setting this too low causes frequent garbage collection, which increases latency and CPU usage. Setting it too high in a container means the JVM can grow past the container memory limit and get OOM-killed by the container runtime before GC has a chance to recover.

Infinispan session caches

Keycloak uses embedded Infinispan to cache active user sessions, offline tokens, and authentication state. Each active session occupies cache memory. In a single-node deployment this cache lives entirely in-process. In a clustered deployment, Infinispan replicates or distributes cache entries across nodes, so each node holds a portion (or all, depending on cache mode) of the total session data.

Active concurrent sessions, not registered user count, determine how much memory the session cache consumes. A deployment where most users log in once and hold a session for 8 hours will have much higher sustained session memory than one where tokens are short-lived and users rarely maintain long sessions.

Metaspace and native memory

Beyond the heap, the JVM uses native memory for the JIT-compiled code cache (code cache), class metadata (metaspace), thread stacks, and internal structures. Quarkus-based Keycloak has significantly reduced the native memory overhead compared to the older WildFly-based distribution, but you still need to leave meaningful headroom above -Xmx for these allocations. A common rule of thumb is to set -Xmx to 70-75% of the container memory limit and leave the rest for native overhead.

What the official Keycloak sizing formula says

Keycloak’s high-availability guide publishes concrete, load-tested sizing figures you can calculate from instead of guess. CPU scales linearly with request rate, and password hashing dominates the cost. The current guidance was measured with the default Argon2 hashing (5 iterations, 7 MiB), a database seeded with 1,000,000 users and 20,000 clients, on OpenJDK 21 (source: Keycloak, “Concepts for sizing CPU and memory resources”):

  • Allocate 1 vCPU for every 15 password-based logins per second (tested up to 300/sec). Because this is dominated by password hashing, a higher hash iteration count lowers the logins-per-vCPU figure.
  • Allocate 1 vCPU for every 120 client-credential grants per second (tested up to 2,000/sec). Most of this CPU goes into establishing TLS connections.
  • Allocate 1 vCPU for every 120 refresh-token requests per second (tested up to 435/sec).
  • Leave roughly 150% extra CPU head-room for spikes, startup, and failover, so set the CPU limit to about 2.5x the requested CPU.
  • Base memory is about 1,250 MB per pod, including realm caches and 10,000 cached sessions. In containers Keycloak uses about 70% of the memory limit for heap plus roughly 300 MB of non-heap memory, so size the memory limit as (expected memory - 300 MB) / 0.7.
  • Database: for every 100 login/logout/refresh requests per second, budget about 1,400 write IOPS and 0.35-0.7 vCPU on the database.

A worked example

For a target of 45 logins/sec, 360 client-credential grants/sec, and 360 refresh-token requests/sec across 3 pods:

  • CPU: 45 ÷ 15 = 3 vCPU (logins) + 360 ÷ 120 = 3 vCPU (client credentials) + 360 ÷ 120 = 3 vCPU (refresh) = 9 vCPU total, or 3 vCPU requested per pod, with a 7.5 vCPU limit per pod after the 150% head-room.
  • Memory: about 1,250 MB requested per pod, with a limit near 1,360 MB ((1,250 – 300) ÷ 0.7).
  • Database: about 410 requests/sec works out to roughly 1.4-2.8 vCPU on the database.

Two caveats. These figures come from a cross-site active/active test, so a single-site deployment can perform slightly better because more sessions stay cached locally. And the logins-per-vCPU number assumes the default Argon2 cost: raising hash iterations for stronger security reduces logins per vCPU proportionally.

Realistic starting points

The table below is a quick-reference approximation for teams that do not yet know their request rates. For real sizing, use the official formula above (1 vCPU per 15 logins/sec, etc.) and the worked example. These tiers are rough starting points; your actual numbers will vary based on hashing iterations, number of realms and clients, session lifetime, token size, and concurrent load. Validate every tier with load testing before committing to production sizing.

Red Hat publishes sizing guidance and a recommended load-testing approach for Red Hat Build of Keycloak (RHBK), which is the supported enterprise distribution of Keycloak. Reading that documentation alongside community operator reports is the best starting point for estimating your resource envelope before you run your own tests.

Deployment tier Replicas vCPU per pod RAM per pod JVM heap (-Xmx) Typical use case
Minimal / dev 1 0.5-1 768 MB – 1 GB 512 MB Non-production, demos
Small production 2 1-2 1-1.5 GB 768 MB Low-to-moderate login rate, single realm
Medium production 2-3 2-4 2-3 GB 1.5 GB Moderate login rate, multiple realms/clients
Large production 3+ 4+ 4+ GB 2-3 GB High login rate, many realms, heavy concurrent sessions

Validate these ranges with load testing before going live. They are starting points, not guarantees.

JVM heap sizing in containers

Getting heap sizing right in Kubernetes or Docker is one of the most common sources of Keycloak instability. The JVM was not originally designed to be container-aware — it would detect total host memory and allocate heap accordingly, ignoring cgroup limits. Modern JVM versions (17+, which Keycloak 26.x requires) are container-aware by default, but explicit configuration is still best practice.

Set an explicit -Xmx in your JAVA_OPTS or JAVA_OPTS_APPEND environment variable:

JAVA_OPTS_APPEND: "-Xmx1536m -Xms512m"

Alternatively, use MaxRAMPercentage to set heap as a fraction of container memory:

JAVA_OPTS_APPEND: "-XX:MaxRAMPercentage=70.0"

Either approach works. The key principle is to leave headroom — typically 25-30% of the container memory limit — for native memory, code cache, metaspace, and thread stacks. A pod with a 2 GB memory limit should have -Xmx no higher than around 1.4-1.5 GB.

Watch GC pressure under load. If you see frequent GC events or long GC pauses in your logs and metrics, the heap may be too small for the workload. If the pod is being OOM-killed, the heap is too large relative to the container limit or native memory consumption is higher than expected.

For a complete Kubernetes deployment reference including resource requests, limits, and liveness probes configured for Keycloak’s startup time, see our Keycloak Kubernetes production deployment guide.

Scaling horizontally vs. vertically

Keycloak scales horizontally via clustering. Multiple pods share session state through Infinispan’s distributed cache, coordinated via JGroups. Adding a replica increases total throughput capacity and provides redundancy — if one pod is OOM-killed or restarted during an upgrade, other pods continue serving requests.

Always run at least two replicas in production. A single Keycloak pod is a single point of failure. A restart — whether from an upgrade, an OOM event, or a node drain — causes an authentication outage for every application that depends on Keycloak. Two replicas allow rolling restarts and absorb transient pod failures without dropping traffic.

Horizontal scaling has limits. Infinispan replication adds network overhead proportional to session volume and write rate. At very high session counts, the cache synchronization overhead can itself become a bottleneck. At that scale, careful tuning of cache ownership counts, distributed vs. replicated cache modes, and even external Infinispan or Valkey clusters becomes relevant. For most production deployments, two to four well-sized pods is sufficient.

Vertical scaling (larger pods) is simpler operationally but provides no redundancy on its own. Use vertical scaling to increase capacity per pod and horizontal scaling for HA and burst capacity.

For cluster configuration best practices including Infinispan tuning, sticky sessions, and health check configuration, see our top 7 Keycloak cluster configuration best practices.

Database sizing matters as much as Keycloak itself

Keycloak’s resource consumption is meaningless in isolation. The PostgreSQL database that backs Keycloak has its own sizing requirements, and a bottlenecked database will saturate Keycloak’s request queues even when Keycloak pods have plenty of CPU and RAM headroom.

The database stores persistent session records, user attributes, group memberships, realm configuration, and audit events. Database CPU pressure typically shows up during high login rates (writes per login) and during session cleanup jobs. RAM pressure shows up as cache eviction in PostgreSQL’s shared_buffers, forcing more disk I/O.

Key considerations for database sizing:

  • Connection pool sizing: Keycloak uses a JDBC connection pool. Each Keycloak pod holds open connections. With multiple replicas, total connections multiply. Ensure PostgreSQL’s max_connections is sized accordingly, and consider using PgBouncer in transaction mode to reduce connection overhead.
  • Index maintenance: Tables like USER_SESSION, OFFLINE_USER_SESSION, and EVENT_ENTITY grow continuously. Routine autovacuum and index maintenance are essential to keep query performance stable over time.
  • Dedicated resources: Do not co-locate the Keycloak database on the same node or VM as the Keycloak pods under production load. Database I/O and Keycloak CPU will compete for the same resources.

For a full treatment of database tuning, see our guide on Keycloak database tuning with PostgreSQL.

How to actually size your deployment

The only reliable way to size Keycloak for your workload is to run a load test that matches your production traffic patterns. Here is a practical approach:

1. Characterize your login mix. Identify the ratio of fresh logins (password hashing, most CPU-intensive) to token refreshes (no hashing, much cheaper) to client credential grants (no user session, cheapest). A deployment that mostly handles service-to-service client credentials flows will size very differently from one serving browser-based user logins.

2. Determine your peak concurrency. What is the maximum number of logins per second or per minute you expect at peak? What is the maximum number of concurrent active sessions? These two numbers are your primary sizing inputs.

3. Run load tests with realistic parameters. Tools like k6, Gatling, or the reference load test tooling referenced in Red Hat’s sizing documentation can simulate realistic login and token-refresh traffic. Run tests against a Keycloak configuration that mirrors production: same hashing iterations, same realm and client count, same token policies.

4. Monitor CPU, heap, GC, and database together. During load tests, watch:

  • CPU utilization on Keycloak pods (should stay well below 100% at target load)
  • JVM heap usage and GC frequency/duration (target: GC pauses infrequent and short)
  • Infinispan cache hit rates and entry counts (watch for eviction under high session load)
  • Database connection wait times and query latency

5. Leave headroom. Size for your expected peak plus a safety margin. Authentication is not a workload where you want to be at 95% CPU on a normal day — an unexpected spike will immediately degrade login performance.

If Keycloak is slow for users in groups, the issue may not be raw CPU or RAM — it can be query patterns on group membership tables. See our post on Keycloak slow login with many groups for a focused diagnosis of that specific problem.

The operational cost of self-managing this

Sizing is a one-time exercise, but maintaining correct sizing over time is an ongoing operational responsibility. Your user base grows. Login patterns shift. New applications onboard. Token policies change. Each of these can silently erode your headroom.

If you are evaluating whether the operational overhead is worth it, our breakdown of what it actually costs to self-host Keycloak covers the full picture including infrastructure, labor, and ongoing maintenance burden.

If you would rather run Keycloak without managing JVM tuning, cluster configuration, and capacity planning yourself, Skycloak’s managed Keycloak hosting handles sizing, upgrades, monitoring, and HA automatically.

Frequently asked questions

How much RAM does Keycloak need?

A single production Keycloak pod commonly starts with 1-1.5 GB of container memory, with the JVM heap bounded to around 70-75% of that limit. Deployments with high concurrent session counts, many realms, or large token payloads will need more. The right answer depends on your active session count and login rate — run load tests to find your actual floor.

Is Keycloak resource-heavy?

No. Keycloak is a moderate resource consumer by enterprise software standards. The Quarkus-based distribution (Keycloak 17 and later, including 26.x) starts significantly faster and uses less memory than the older WildFly-based versions. The main cost drivers — password hashing and active session caching — are proportional to your actual usage, not to a large baseline overhead.

How many users can one Keycloak instance handle?

Total registered users is largely irrelevant to resource sizing. A Keycloak instance can hold millions of registered users in its database without those users consuming meaningful CPU or RAM — those records are stored in PostgreSQL. What matters is how many users are authenticating concurrently and how many active sessions are in memory at any given time. A two-pod cluster can comfortably handle thousands of concurrent active sessions and hundreds of logins per minute for many organizations, but your specific numbers depend on your configuration and workload mix.

Should I run one large Keycloak pod or multiple smaller pods?

Multiple smaller pods are almost always preferable for production workloads. Two or more replicas provide high availability, allow rolling upgrades without downtime, and distribute CPU load across nodes. A single large pod, regardless of how much RAM and CPU it has, is a single point of failure. Start with two pods sized appropriately for half your expected peak load (with headroom), and add replicas as load grows.

What happens to Keycloak performance under high hashing iterations?

Each login requires one full hash computation at the configured iteration count. At very high iteration counts (set to increase security), a modest number of concurrent logins can push CPU to 100% on an undersized pod. This is by design — the cost is what makes brute-force attacks impractical. If you see CPU saturation during normal login events, check your hashing configuration in the realm password policy and compare it to Keycloak’s recommended defaults for your version. Increasing pod count or vCPU allocation is the correct response, not reducing security by lowering iteration counts below the recommended minimum.

Tired of running Keycloak yourself?

Skycloak runs real upstream Keycloak for you with a 99.99% SLA. No fork, no lock-in, just managed Keycloak that stays patched and on call so you don't have to.

Guilliano Molaire
Written by
Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Start Free Trial Talk to Sales
© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman