Keycloak resource requirements for a homelab or small team come down to three numbers: a 2 GB container memory limit (750 MB is the documented floor), 2 vCPU, and a separate PostgreSQL database on SSD. Keycloak’s own sizing guide puts baseline usage at 1250 MB of RAM per instance. A four-core N100 mini-PC runs it with plenty to spare.
Past that, the numbers that matter are request rates, not user counts. A household generating a few logins an hour is nowhere near needing a second core. Memory is the line you have to set deliberately, because the JVM takes whatever you leave unbounded.
Keycloak resource requirements at a glance
| Resource | Documented figure | On one mini-PC |
|---|---|---|
| Container memory limit | 2 GB for smaller production deployments; 750 MB approximates the old 512 MB heap | Set 2 GB |
| Baseline RAM per instance | 1250 MB, including realm caches and 10,000 cached sessions | What a quiet instance uses |
| Heap and non-heap | Heap up to 70% of the container limit; about 300 MB non-heap | Always set a limit |
| CPU, password logins | 1 vCPU per 15 logins per second | Not your bottleneck |
| CPU, refresh and client credentials | 1 vCPU per 120 requests per second | Not your bottleneck |
| CPU headroom | 150% extra for spikes, startup and failover | Don’t cap CPU tightly |
| Database | PostgreSQL 14 to 18 supported; dev-file not for production | Postgres, even for one user |
| Database load | 1400 write IOPS and 0.35 to 0.7 vCPU per 100 login, logout or refresh requests per second | Local SSD, not a NAS share |
| Instance count | Two or more in typical production | One, if you accept SSO downtime |
Memory and CPU figures come from Keycloak’s sizing guide and container guide, the database rows from the database guide, and the instance count from the production configuration guide.
Those sizing numbers came from OpenShift on AWS Graviton c7g instances against Aurora PostgreSQL seeded with 1,000,000 users and 20,000 clients, and the guide says to “use these values as a starting point and perform your own load tests before going into production.” They describe a cloud cluster, but they are still the best published yardstick for a box under your desk.
How much RAM does Keycloak need?
Plan for a 2 GB memory limit per instance. The official image sizes the JVM heap from the container limit (-XX:MaxRAMPercentage=70, -XX:InitialRAMPercentage=50), so the limit decides how big the heap can grow. The container guide recommends a 2 GB limit for smaller production deployments and says at least 750 MB approximates the old 512 MB maximum heap.
Its more important instruction: always set a limit. Without one, the heap can grow to 70% of the memory the container sees, which on a bare docker run means 70% of the host. That is a lot of RAM for a login page.
The two official documents disagree slightly. The sizing guide’s worked example lands on a 1360 MB limit: 1250 MB of expected usage, minus 300 MB non-heap, divided by 0.7. That example keeps user sessions in the database, while the 2 GB figure buys slack for realm imports, extensions and cache growth. On a 16 GB mini-PC the extra 640 MB costs nothing. On a 2 GB VPS shared with other services, 1360 MB is a defensible squeeze.
For a fixed heap instead of percentages, the container guide documents the JAVA_OPTS_KC_HEAP override. Cache caps and the metrics worth watching are covered in Keycloak memory usage and heap sizing.
How much CPU does Keycloak need?
Two vCPU is a sensible floor, and the reason is startup and password hashing, not steady-state traffic. The sizing guide budgets 1 vCPU per 15 password logins per second because Keycloak “spends most of the CPU time hashing the password provided by the user,” using default Argon2 with 5 iterations and a 7 MiB minimum memory size.
In homelab terms, 15 logins per second is 54,000 an hour on one core. Refresh tokens and client credential grants are cheaper, at 120 per second per vCPU. Your Jellyfin and Grafana logins will not register.
Where you feel CPU at home is restarts. Running start on the stock image applies build options at boot, which the container guide says “significantly increases startup time.” The fix is a two-stage image that runs kc.sh build once and then start --optimized:
FROM quay.io/keycloak/keycloak:26.8.0 AS builder
ENV KC_HEALTH_ENABLED=true
ENV KC_DB=postgres
RUN /opt/keycloak/bin/kc.sh build
FROM quay.io/keycloak/keycloak:26.8.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
This is optional. If Keycloak restarts once a month after an update, a slower boot is a minor annoyance, and the custom image is one more thing to rebuild.
Two cautions. The sizing guide says “performance of Keycloak dropped significantly when its Pods were throttled,” so don’t set cpus: 1 just to be tidy. And the per-vCPU figures aren’t portable to a 6 W chip in either direction: the guide saw x86 instances deliver up to twice the refresh and client credential operations per core of the Graviton machines, with password hashing roughly constant per core.
What does Keycloak need from the database?
A real PostgreSQL instance on fast local storage. The database guide says the bundled dev-file database “is not suitable for production use-cases,” and lists PostgreSQL 14.x through 18.x as supported, alongside MariaDB, MySQL, SQL Server and Oracle. Pick Postgres unless you already run one of the others.
The database matters more than it used to. Per the caching guide, persistent user sessions are the default, with sessions “loaded on-demand from the database into the embedded caches,” so every login is a database write. The sizing guide budgets 1400 write IOPS per 100 login, logout or refresh requests per second. You won’t approach that at home, but write latency is login latency, so pgdata belongs on the mini-PC’s NVMe drive, not on an NFS export from the NAS.
db-pool-max-size defaults to 100 connections. That is fine for one instance. If you ever run several against one Postgres, multiply by the instance count and check it against Postgres’s connection limit.
Disk on the Keycloak side is close to irrelevant; the database is the state.
Hardware that works for a single-box install
An Intel N100 mini-PC is the default homelab box for good reason. Intel’s spec sheet lists 4 cores, 4 threads, a 6 W TDP and a 16 GB maximum memory size. Give Keycloak 2 GB and Postgres a 1 GB guardrail, and roughly 13 GB is left for everything else you swore you would not self-host.
The NAS is the backup target, not the database host. Dump Postgres nightly to an SMB or NFS share and periodically restore a dump into a scratch container, because a backup you have never restored is a theory.
A compose file with the limits set
This assumes the Containerfile above sits next to compose.yml, a reverse proxy on the host terminates TLS, and PG_PASS and KC_ADMIN_PASS live in .env.
services:
postgres:
image: docker.io/library/postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${PG_PASS:?set PG_PASS in .env}
volumes:
- ./pgdata:/var/lib/postgresql/data
mem_limit: 1g
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"]
interval: 30s
retries: 5
keycloak:
build: .
image: local/keycloak:26.8.0
command: start --optimized
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
KC_DB_URL_HOST: postgres
KC_DB_URL_DATABASE: keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${PG_PASS}
KC_HOSTNAME: https://auth.example.com
KC_HTTP_ENABLED: "true"
KC_PROXY_HEADERS: xforwarded
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KC_ADMIN_PASS:?set KC_ADMIN_PASS in .env}
ports:
- "127.0.0.1:8080:8080"
mem_limit: 2g
healthcheck:
test: ["CMD", "bash", "-c", "{ printf 'HEAD /health/ready HTTP/1.0\\r\\n\\r\\n' >&0; grep -q 'HTTP/1.0 200'; } 0<>/dev/tcp/localhost/9000"]
interval: 30s
retries: 5
start_period: 90s
mem_limit: 2g is the line that matters, and there is deliberately no cpus: cap on Keycloak. The healthcheck uses bash’s /dev/tcp because the image ships without curl, as the health guide explains, and it targets management port 9000, which stays unpublished. If the proxy throws 403s or redirect loops, that is configuration rather than resources; Keycloak hostname and proxy errors walks through the fixes.
When does one box stop being enough?
When SSO downtime costs you more than a second machine does, not when CPU runs out. The production guide says “a typical production environment contains two or more Keycloak instances” so users can still log in when one goes down. For a homelab, a five-minute outage during an update is an acceptable price.
For reference, the sizing guide’s cluster example targets 45 logins and logouts, 360 client credential grants and 360 refresh requests per second. It lands on three pods with 3 vCPU requested and a 1360 MB memory limit each, plus a 2 or 4 vCPU database. If your traffic is nowhere near that, clustering buys you Infinispan, a load balancer and a shared database to babysit. That is more work than it is worth unless you wanted to learn it anyway.
If a 2 GB JVM feels heavy for what it protects, Keycloak vs authentik vs Authelia covers when a lighter tool fits better.
FAQ
can keycloak run on 1gb of ram
Yes, for a handful of users, but with no slack. Keycloak’s container guide says a memory limit of at least 750 MB approximates the old 512 MB heap default, so a 1 GB limit gives the JVM roughly 700 MB of heap. That sits below the documented 1250 MB baseline with full caches, so expect more database reads.
is keycloak too heavy for a homelab
Not on a mini-PC with 8 GB of RAM or more. Budget 2 GB for Keycloak and a modest slice for PostgreSQL, and there is room left for the rest of the stack. It is too heavy when you only need forward-auth in front of a few web apps on a small VPS, where a lighter tool fits better.
how many users can keycloak handle on one server
There is no fixed user ceiling, because Keycloak sizes by request rate rather than user count. The sizing guide’s test database held 1,000,000 users and 20,000 clients, and CPU was budgeted at 15 password logins per second per vCPU. Stored users cost database rows and cache entries, not CPU, until they log in.
how much disk space does keycloak need
Very little on the Keycloak side, because the container holds no state worth keeping; everything lives in PostgreSQL. Database size grows with users, clients, persisted sessions and, if you enable saving them, user and admin events. Keep the Postgres data directory on local SSD and back it up to the NAS nightly.