Keycloak
Identity and access management server: single sign-on over OpenID Connect, OAuth 2.0 and SAML for your apps, with realms, user federation (LDAP, Kerberos), social and enterprise login, two-factor authentication and an admin console. The self-hosted stack is one Java (Quarkus) server and PostgreSQL, which holds all of its state.
Resources by load level
What the app itself needs at each level, next to what we recommend for a server that runs only this app.
| Level | What it means | App requirements | Recommended |
|---|---|---|---|
| LightEstimate | Up to 1,000 users in a few realms and ~10 client apps; peaks of 2 password logins and 24 token refreshes a second. | 2 vCPU · 3 GiB RAM · 7.5 GiB disk | 2.52 vCPU · 5.1 GiB RAM · 18.055 GiB disk |
| MediumEstimate | Up to 10,000 users; peaks of 6 password logins, 48 token refreshes and 12 client credential grants a second. | 3.25 vCPU · 3 GiB RAM · 7.5 GiB disk | 4.02 vCPU · 5.1 GiB RAM · 18.055 GiB disk |
| HeavyEstimate | Up to 50,000 users; peaks of 12 password logins, 96 token refreshes and 24 client credential grants a second. | 6.5 vCPU · 6 GiB RAM · 52.5 GiB disk | 7.92 vCPU · 8.7 GiB RAM · 69.805 GiB disk |
Recommended = app requirements + system baseline + safety headroom, under sizing policy 2026.10.0. Options stay at their defaults. “Estimate” means the sources give no number for that case, so it is worked out from them by the method stated under Components. How the numbers are built
Ready-to-run files
Support level Supported
Compose template from the official sources; not yet verified by a run.
Components
What each component needs at each load level, before baseline and headroom.
| Component | Light | Medium | Heavy | Sources |
|---|---|---|---|---|
| Keycloak server (app) | 1 / 2 / 1 | 2.25 / 2 / 1 | 4.5 / 2 / 1 | [2][13][14][15][31] |
| PostgreSQL (database) | 1 / 1 / 6.5 | 1 / 1 / 6.5 | 2 / 4 / 51.5 | [7][8][14][15][35][36][37][38][39][40][41] |
How the estimates are worked out
- Keycloak server · Light: CPU by the sizing guide: 2 logins/s ÷ 15 + 24 refreshes/s ÷ 120 = 0.333 vCPU, plus 150% headroom = 0.833, rounded up to 1 vCPU. RAM: the container guide's 2 GB for small production (base use 1250 MB with caches and 10,000 sessions); heap pinned at 1434 MiB. Disk: 265,677,312 B compressed image × 3 = 0.742 GiB, rounded up to 1 GiB; no data on disk.
- Keycloak server · Medium: CPU by the sizing guide: 6 ÷ 15 + 48 ÷ 120 + 12 ÷ 120 = 0.9 vCPU (refreshes at the guide's 1:8 login ratio), plus 150% headroom = 2.25 vCPU. RAM stays at 2 GiB: memory follows the cached data (10,000 entries per cache by default), not the request rate. Disk: 0.742 GiB image, rounded up to 1 GiB.
- Keycloak server · Heavy: CPU by the sizing guide: 12 ÷ 15 + 96 ÷ 120 + 24 ÷ 120 = 1.8 vCPU, plus 150% headroom = 4.5 vCPU; password hashing takes most of it. RAM stays at 2 GiB: the guide's own example serves 45 logins/s per cluster with 1250 MB per node. Disk: 0.742 GiB image, rounded up to 1 GiB.
- PostgreSQL · Light: ~26 login, logout and refresh requests/s × 0.35–0.7 vCPU per 100 (Keycloak's database measurements) = 0.09–0.18 vCPU over a 0.3 vCPU idle load, inside the sidecar's 1 vCPU; ~364 write IOPS. The pool is capped at 20 connections. Light preset method: shared_buffers 25% of RAM, 0.5 GiB image + 5 GiB data + 1 GiB WAL.
- PostgreSQL · Medium: ~55 login, logout and refresh requests/s × 0.35–0.7 vCPU per 100 = 0.19–0.39 vCPU over a 0.3 vCPU idle load, still inside 1 vCPU; ~770 write IOPS. 10,000 users × an assumed 20 KB of rows = 0.2 GB, inside 5 GB; pool capped at 20. Light preset method: shared_buffers 25% of RAM, 0.5 GiB image + 5 GiB data + 1 GiB WAL.
- PostgreSQL · Heavy: ~110 login, logout and refresh requests/s × 0.35–0.7 vCPU per 100 = 0.38–0.77 vCPU over a 0.3 vCPU idle load: up to 1.07 vCPU, past the sidecar's 1 vCPU, so the PostgreSQL profile's medium preset (2 vCPU, 4 GiB). ~1,540 write IOPS. 50,000 users × an assumed 20 KB = 1 GB of rows.
Ports
Public ports are meant to be reached from outside the stack, usually through a reverse proxy; internal ones stay on the stack's own network.
| Component | Port | Exposure | Purpose | Sources |
|---|---|---|---|---|
| Keycloak server | 8080/tcp | Public, through a proxy | Login pages, OIDC/SAML endpoints, Admin Console and Admin API (HTTP; TLS at the proxy) | [5][11] |
| Keycloak server | 9000/tcp | Internal only | Management interface: /health and /metrics; never expose it | [3][4] |
| Keycloak server | 7800/tcp | Internal only | JGroups unicast transport of the ispn caches (jdbc-ping stack) | [10] |
| Keycloak server | 57800/tcp | Internal only | JGroups failure detection (FD_SOCK2, bind port + 50000) | [10] |
| PostgreSQL | 5432/tcp | Internal only | PostgreSQL connections from Keycloak (also its jdbc-ping node registry) | [7][40] |
User and permissions (UID/GID)
The user each container's process runs as, and the paths it writes to. A bind mount for one of these paths must be writable by that UID:GID.
| Component | UID:GID | User | Starts as root | Writes to | Sources |
|---|---|---|---|---|---|
| Keycloak serverDerived | 1000:0 | keycloak | No | /opt/keycloak/data/opt/keycloak/lib | [11][16][17] |
| PostgreSQL | 999:999 | postgres | Yes, prepares its directories, then drops to this user | /var/lib/postgresql/var/run/postgresql | [33][34][40] |
- Keycloak server: USER 1000 (keycloak, group 0) from the start, with /opt/keycloak owned by 1000:0 and group-writable. data/ holds runtime files; start without --optimized writes its build into lib/. Neither needs a volume: realms, users and sessions live in PostgreSQL.
Storage growth
How much each component's data grows per month at each load level, on top of the disk in the profile. A value marked Assumption is ANAROQ's stated assumption, not a figure from the sources; the notes say what it rests on.
What the growth figures rest on
- PostgreSQL: New users a month (an assumed 50, 500 and 2,500) × an assumed 20 KB of rows each. Sessions are persisted but expire, so they hold a steady size; stored user and admin events are off by default and, once on, grow until their expiration removes them.
Upgrade and rollback
How to move to a new version safely, and how to go back if it fails.
Back up first
Take a pg_dump of the keycloak database and keep the .env: the migrated schema no longer works with the old server. [22]
Upgrade steps
- Read the upgrade notes of every version between yours and the new one in the Upgrading Guide: breaking changes are listed per release. [21]
- Change the pinned keycloak tag, then run docker compose pull and docker compose up -d. Compose stops the old container first, as minor and major upgrades require; the schema migrates at the first start (30 minute timeout). Follow docker compose logs -f keycloak until it is healthy. [22][23]
- On tables over 300,000 rows the migration skips new indexes and PostgreSQL builds them in the background after startup; expect extra database load for a while. [23]
Roll back
Keycloak cannot roll back database changes: stop it, restore the dump from before the upgrade, and pin the image back to the previous tag. [21][22]
Known breaking changes
- In 26.8.1: With Enable StartTLS on, SMTP now requires a successful STARTTLS upgrade instead of falling back to plaintext; email fails against servers without STARTTLS. [24]
- In 26.8.0: Login failures for brute force detection are stored in the database by default, with more database connections and CPU; disabled clients no longer appear in token audiences. [25]
- In 26.7.0: The view-system admin role was removed, the shutdown timeout grew from 1 s to 10 s, and ephemeral writes (sessions, events) use PostgreSQL async commit. [26]
- In 26.0.0: KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD gave way to KC_BOOTSTRAP_ADMIN_USERNAME and KC_BOOTSTRAP_ADMIN_PASSWORD; hostname v1 and the proxy option were removed, and all user sessions are persisted in the database. [27]
Limits
- One Keycloak node. CPU follows password hashing: 1 vCPU per 15 password logins a second, plus 150% headroom. The heap is pinned at 1434 MiB for the 2 GiB preset; change JAVA_OPTS_KC_HEAP if you give Keycloak more memory. [2][13][14]
- Sessions live in PostgreSQL: every 100 login, logout and refresh requests a second need about 1,400 write IOPS, so a slow disk limits throughput before CPU does. [7][15]
- The stock image runs its build at every start, which makes starts slower. Custom providers, themes or build options such as features need an optimized image of your own, which the template does not build. [2][16]
Security
- The admin from .env is temporary and exists only to get in: sign in, create a permanent admin with two-factor authentication, then delete the temporary account. The bootstrap variables are ignored once the master realm exists. [16][18]
- The proxy forwards everything. The reverse proxy guide recommends exposing only /realms/, /resources/ and /.well-known/, and keeping /admin/ and /realms/master/ internal: block them at your proxy, or use the master realm only from a private network. [5][6]
- Behind a proxy Keycloak trusts X-Forwarded-* headers (KC_PROXY_HEADERS=xforwarded). Your proxy must set and overwrite them, and port 8080 must not be reachable around it; health and metrics stay on the unpublished port 9000. [4][5]
- Brute force detection is off by default. Turn it on in each realm (Realm settings, Security defenses) before exposing login pages. [19]
Backup
- All state is in PostgreSQL: realms, users, credentials, clients, signing keys and sessions. Back up a pg_dump of the keycloak database together with the .env; the Keycloak container has no volume. [7][22]
- Realm exports from the Admin Console are not backups: they leave out secrets and users. Only exports taken at boot (kc.sh export) are fit for backups or moving to another server. [19][20]
Latest release
- This profile was checked against
Keycloak 26.8.1
This profile was checked against the newest release.
- Checked on
- 14 Oct 2026
- State
- Checked within the last 90 days
- Where releases are published
- GitHub Releases
Version
- Profile status
- Published
- Checked against release
- Keycloak 26.8.1
- Profile version
- 1.0.0
- Figures verified
- 14 Oct 2026
- Catalog release
- 2026.10.10
- Sizing policy
- 2026.10.0
- Compose file
- Unverified
Project and alternatives
- Official site
- www.keycloak.org
- Source code
- github.com/keycloak/keycloak
Sources
- Docker Docs: Control startup order in Compose (pg_isready healthcheck, service_healthy) (Official docs · read 11 Oct 2026)
- Keycloak Docs: Running Keycloak in a container (plain start re-runs the build at every start; KC_BOOTSTRAP_ADMIN_*; heap 70% of the memory limit, 2 GB recommended for small production) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Health checks (/health/ready on management port 9000 with health-enabled; no curl in the image; bash /dev/tcp HEALTHCHECK example) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Management interface (/health and /metrics on port 9000, kept off the main HTTP port) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Configuring the hostname v2 (hostname as a full URL, http-enabled for edge TLS on 8080, proxy-headers xforwarded) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Configuring a reverse proxy (expose /realms/, /resources/, /.well-known/; keep /admin/ and /realms/master/ internal; proxy must overwrite X-Forwarded-*) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Configuring the database (PostgreSQL 18.x to 14.x supported, tested with 18; db-url, db-username, db-password; connection pool) (Official docs · read 14 Oct 2026)
- Keycloak Docs: All configuration (bootstrap-admin-username temp-admin, db-pool-max-size 100, http-enabled false, cache ispn, health-enabled false, management port 9000) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Configuring Keycloak for production (with health enabled, ports open during bootstrap and /health/ready stays DOWN until it ends) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Configuring distributed caches (start uses ispn with the jdbc-ping stack; ports 7800 and 57800; local cache only for development) (Official docs · read 14 Oct 2026)
- keycloak repository: quarkus/container/Dockerfile at 26.8.1 (ubi9-micro, /opt/keycloak owned 1000:0, USER 1000, EXPOSE 8080 8443 9000, ENTRYPOINT kc.sh, no HEALTHCHECK) (Official repository · read 14 Oct 2026)
- keycloak repository: quarkus/container/ubi-null.sh at 26.8.1 (the image keeps bash and coreutils-single for a shell; no curl) (Official repository · read 14 Oct 2026)
- keycloak repository: kc.sh at 26.8.1 (in a container, JAVA_OPTS_KC_HEAP defaults to MaxRAMPercentage=70 and InitialRAMPercentage=50; a set value replaces it) (Official repository · read 14 Oct 2026)
- Keycloak Docs: Concepts for sizing CPU and memory (1250 MB base; 1 vCPU per 15 password logins/s, per 120 refreshes/s, per 120 client credential grants/s; 150% CPU headroom) (Official docs · read 14 Oct 2026)
- keycloak repository: perf_recommendations.adoc at 26.8.1 (database: 1400 write IOPS and 0.35 to 0.7 vCPU per 100 login/logout/refresh per second; idle 0.3 vCPU) (Official repository · read 14 Oct 2026)
- Keycloak Docs: Configuring Keycloak (start runs a build under the covers; --optimized skips it; bootstrap admin variables are ignored once an admin exists) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Directory structure (/opt/keycloak in containers; data/ holds runtime information such as transaction logs; lib/ used internally) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Bootstrapping and recovering an admin account (bootstrap admin is temporary, created only with the master realm; remove it manually) (Official docs · read 14 Oct 2026)
- Keycloak Server Administration Guide (brute force detection off by default; Save events off until enabled; Admin Console exports are not backups) (Official docs · read 14 Oct 2026)
- Keycloak Docs: Importing and exporting realms (kc.sh export with users; Admin Console partial export leaves out users and masks secrets) (Official docs · read 14 Oct 2026)
- Keycloak Upgrading Guide (back up the database first; schema migrates at first start; database changes cannot be rolled back) (Official docs · read 14 Oct 2026)
- keycloak repository: upgrading/topics/prep_migration.adoc at 26.8.1 (shut down for minor and major upgrades; back up the database; no rollback of schema changes) (Official repository · read 14 Oct 2026)
- keycloak repository: upgrading/topics/migrate_db.adoc at 26.8.1 (automatic migration, 30 min timeout, indexes on tables over 300,000 rows built in the background) (Official repository · read 14 Oct 2026)
- Keycloak 26.8.1 upgrade notes (SMTP StartTLS is required when enabled, CVE-2026-107121) (Release notes · read 14 Oct 2026)
- Keycloak 26.8.0 upgrade notes (login failures stored in the database by default; disabled clients left out of token audiences) (Release notes · read 14 Oct 2026)
- Keycloak 26.7.0 upgrade notes (view-system admin role removed; shutdown timeout 10 s; async commit on PostgreSQL) (Release notes · read 14 Oct 2026)
- Keycloak 26.0.0 upgrade notes (KEYCLOAK_ADMIN replaced by KC_BOOTSTRAP_ADMIN_*; hostname v1 and proxy option removed; sessions persisted) (Release notes · read 14 Oct 2026)
- Keycloak Release Notes (26.8.0 is the newest minor release) (Release notes · read 14 Oct 2026)
- Keycloak: Downloads (26.8.1 server distribution and container image) (Official docs · read 14 Oct 2026)
- Quay tags of quay.io/keycloak/keycloak (newest release 26.8.1, pushed 2026-10-13; 26.8 and latest point to the same index) (Official repository · read 14 Oct 2026)
- Quay manifest of quay.io/keycloak/keycloak:26.8.1 (linux/amd64): 4 layers, 265,677,312 B compressed; config User 1000, no Healthcheck (Measurement · read 14 Oct 2026)
- keycloak repository: LICENSE.txt at 26.8.1 (Apache License 2.0) (Official repository · read 14 Oct 2026)
- docker-library/postgres: 18/trixie Dockerfile (postgres uid/gid 999, VOLUME /var/lib/postgresql) (Official repository · read 14 Oct 2026)
- docker-library/postgres: 18/trixie docker-entrypoint.sh (chown PGDATA, then gosu postgres) (Official repository · read 14 Oct 2026)
- PostgreSQL 18 documentation: 19.4 Resource Consumption (Official docs · read 10 Oct 2026)
- PostgreSQL 18 documentation: 19.3 Connections and Authentication (Official docs · read 10 Oct 2026)
- PostgreSQL 18 documentation: 19.10 Vacuuming (autovacuum settings) (Official docs · read 10 Oct 2026)
- PostgreSQL 18 documentation: 19.5 Write Ahead Log (max_wal_size) (Official docs · read 10 Oct 2026)
- PostgreSQL 18 documentation: 51.2 How Connections Are Established (Official docs · read 10 Oct 2026)
- Docker Official Image postgres: documentation (docker-library/docs) (Official docs · read 10 Oct 2026)
- Docker Hub: library/postgres tag 18.6 (compressed image sizes) (Official repository · read 10 Oct 2026)