Skip to content

Architecture

The platform splits cleanly into a data plane (Keycloak itself) and a control plane (everything around it), with tenants packed into cells and an Aurora PostgreSQL source of truth.

Default Architecture — Single Region, Multi-AZData plane (Fargate)Control plane (Lambda)DatabaseUsers / SPAs / APIsCloudFront (edge)TLS · WAF / Shield · cache /resources/*DATA PLANE — AWS us-east-1 · Fargate (JVM Keycloak / Quarkus, autoscaled on CPU)Regional ALB (L7)Availability Zone 1Availability Zone 2KC task 1KC task 2KC task 3KC task n◄── clustered via JDBC_PING · embedded Infinispan (in-region) ──►Aurora PostgreSQL — Multi-AZprimary + synchronous replica · source of truthCONTROL PLANE — serverless (Lambda)Provisioning APIBilling / metering (Stripe)AI-config serviceAnalytics pipeline → ClickHouseNotifications (SES / SNS)STS token-brokerTenant Registry + Placement EngineAdmin REST APINot Lambda for Keycloak (stateful/clustered) · not GraalVM native (SPIs power analytics, adaptive auth, STS)Scales two ways · Availability by tierMany tenants → add cells(cell = Fargate + DB + cache, N realms)One huge realm → dedicated Fargatesized by login rate (~1 vCPU / 15 logins/s)99.9% single-region · 99.95% multi-region active-passive · 99.99% active-active multi-cloudSLA tiers backed after production maturity

Keycloak runs as long-running containers on AWS Fargate in JVM mode (Quarkus), clustered across Availability Zones via JDBC_PING, fronted by CloudFront (TLS, WAF/Shield, edge cache for /resources/*) and a regional ALB. Tasks autoscale on CPU.

Everything around Keycloak runs on Lambda and drives Keycloak through its Admin REST API (never by touching the ~90-table schema directly):

Provisioning & Placement

Tenant Registry + Placement Engine decide where a tenant lives and create its realm.

AI-config service

Plain-English changes → Admin REST with a dry-run diff. See Realm Setup.

Analytics pipeline

Event Listener SPI → queue → ClickHouse.

Notifications

Email/SMS via SES/SNS.

STS token-broker

Exchange Keycloak tokens for cloud credentials. See Advanced Security.

A cell is a self-contained Keycloak unit — a Fargate cluster + its database + its cache in a region — bounded to a set number of realms. You scale by adding cells, and a placement engine routes each tenant to one (by plan, region/residency, and required capabilities such as multi-cloud).

Cell-Based Multi-TenancyCloudFront + Edge Routerresolve tenant → cell (host / path / custom domain)Tenant Registrytenant → model · cell · realm · regionPlacement Enginecapacity + capability awarePooled Cell A — up to N realmsKeycloak cluster (Fargate, multi-AZ)Aurora (default)cell databaseInfinispanin-region cachePooled Cell B — up to N realmsKeycloak cluster (Fargate, multi-AZ)Aurora (default)cell databaseInfinispanin-region cacheDedicated Silo (Enterprise)Tenant's own Keycloak + own DBstrongest isolation · own keys / upgradesoptional multi-cloud (Yugabyte) cellThree isolation tiers — used together, not either/orOrganizationsOrg inside a shared realmHighest densityMany small B2B tenantsdensity ▲ isolation ▽Realm-per-tenant (pooled)A realm on a shared clusterStrong isolation, own keysThe standard tierbalancedInstance-per-tenant (silo)Dedicated KC + DBStrongest isolationEnterprise / regulateddensity ▽ isolation ▲
Tier What it is Density Isolation Use for
Organizations Org inside a shared realm Highest Lowest Many small B2B tenants
Realm-per-tenant (pooled) A realm on a shared cluster Medium Strong (own keys/users/flows) The standard tier
Instance-per-tenant (silo) Dedicated Keycloak + DB Lowest Strongest Enterprise / regulated

Aurora PostgreSQL Multi-AZ (primary + synchronous replica) is the source of truth for realms, clients, users, credentials, and — with persistent-user-sessions (KC 26 default) — sessions. Infinispan is an in-region near-cache in front of it, not the system of record, so a cache wipe or node loss doesn’t lose sessions. Only the database (plus tiny cache-invalidation signals) ever crosses regions. See High Availability.

Many tenants → more cells

Horizontal scale for tenant count: add cells as realm counts approach the measured per-cell ceiling.

One huge realm → dedicated Fargate

A single large realm (e.g. 1M users) gets its own Fargate service sized by login rate (~1 vCPU per 15 logins/sec — password hashing is the CPU cost), not by user count. 1M stored users is trivial; active load drives sizing.