TL;DR — Quick Summary

Complete Keycloak guide: install with Docker or Kubernetes, configure realms, OpenID Connect and SAML clients, LDAP federation, RBAC, and high availability.

Keycloak is the leading open-source Identity and Access Management (IAM) solution — it delivers Single Sign-On (SSO), MFA, social login, LDAP/Active Directory federation, and fine-grained authorization using OpenID Connect, OAuth 2.0, and SAML 2.0. This guide covers everything from a production Docker Compose setup to realm configuration, RBAC, identity federation, and high availability clustering.

Prerequisites

  • Docker 25+ and Docker Compose v2 (or a Kubernetes cluster for operator installs).
  • PostgreSQL 15+ for production (embedded H2 is development-only).
  • A reverse proxy (Nginx or Traefik) for TLS termination in production.
  • Basic familiarity with OAuth 2.0 concepts (client, authorization code flow, tokens).

Keycloak Architecture

Keycloak organizes everything into realms — isolated security domains. Each realm contains:

ConceptDescription
RealmIsolated tenant. Owns users, clients, roles, groups, IdPs
ClientApplication registered to use Keycloak for auth
UserIdentity within the realm (local or federated)
RolePermission label. Realm roles span all clients; client roles are scoped
GroupCollection of users with inherited role assignments
Identity ProviderExternal IdP (Google, GitHub, SAML, LDAP)
Authentication FlowConfigurable pipeline: browser login, OTP, WebAuthn

The master realm is the super-admin realm — use it only for managing other realms, never for application users.


Step 1: Install Keycloak with Docker Compose

This is the recommended production-ready starting point. Save as docker-compose.yml:

version: "3.9"
services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: changeme
    volumes:
      - postgres_data:/var/lib/postgresql/data

  keycloak:
    image: quay.io/keycloak/keycloak:24.0
    command: start
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: changeme
      KC_HOSTNAME: auth.example.com
      KC_PROXY: edge          # TLS terminated at reverse proxy
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: adminpass
    ports:
      - "8080:8080"
    depends_on:
      - postgres

volumes:
  postgres_data:
docker compose up -d
# Access Admin Console at http://localhost:8080

For Kubernetes, use the official Keycloak Operator:

kubectl apply -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.0/kubernetes/keycloaks.k8s.keycloak.org-v1.yml
kubectl apply -f https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.0/kubernetes/keycloak-operator.yml

Step 2: Realm Configuration

After logging in to the Admin Console:

  1. Click the realm dropdown (top-left) → Create Realm.
  2. Set a name (e.g., myapp), enable it, click Create.

Key realm settings to configure:

SettingRecommended value
Display nameYour brand name
Login themekeycloak (or custom)
Require SSLexternal requests (production)
SSO Session Idle30 minutes
Access Token Lifespan5 minutes
Refresh Token Lifespan30 minutes

Configure SMTP under Realm Settings → Email for password reset and verification emails.


Step 3: OpenID Connect Client Configuration

Go to ClientsCreate client:

  • Client type: OpenID Connect
  • Client ID: myapp-client
  • Client authentication: ON (confidential) for server-side apps; OFF (public) for SPAs/mobile
  • Valid redirect URIs: https://myapp.example.com/callback
  • Web origins: https://myapp.example.com (enables CORS)

Confidential vs Public Clients

FeatureConfidentialPublic
Has client secretYesNo
Use caseServer-side (Node, .NET, Python)SPA, mobile app
Auth code flowWith client secretWith PKCE
Risk if compromisedHigh — rotate secretLow — secret-less

Protocol Mappers

Add mappers to include custom claims in tokens: Clients → your client → Client ScopesAdd mapper. Common mappers: User Attribute, Role, Group Membership, Audience.


Step 4: Role-Based Access Control (RBAC)

Realm Roles vs Client Roles

Realm roles apply across all clients in the realm. Client roles are scoped to a specific client:

# Create a realm role via kcadm.sh
kcadm.sh config credentials \
  --server http://localhost:8080 \
  --realm master \
  --user admin --password adminpass

kcadm.sh create roles -r myapp -s name=app-admin
kcadm.sh create roles -r myapp -s name=app-user

Groups and Role Mappings

  1. RealmGroupsCreate group → name it Administrators.
  2. Open the group → Role MappingAssign role → select app-admin.
  3. Add users to the group — they inherit the role automatically.

Composite Roles

A composite role bundles other roles. Create a superuser realm role, then add app-admin and app-user as composites — any user with superuser gets both.


Step 5: Identity Federation

Social Login (Google, GitHub, Microsoft)

Identity ProvidersAdd provider → select Google:

  1. Create an OAuth 2.0 app in Google Cloud Console.
  2. Set Client ID and Client Secret from Google.
  3. Set Redirect URI back to Keycloak: https://auth.example.com/realms/myapp/broker/google/endpoint.
  4. Enable Trust Email if Google-verified emails should be auto-trusted.

LDAP / Active Directory Federation

User FederationAdd providerLDAP:

SettingValue
VendorActive Directory
Connection URLldap://ad.corp.example.com
Bind DNCN=svc-keycloak,OU=ServiceAccounts,DC=corp,DC=example,DC=com
Users DNOU=Users,DC=corp,DC=example,DC=com
Sync ModeFORCE (attributes always overwritten from LDAP)

Run Synchronize all users to import AD users into Keycloak. LDAP group mapper syncs AD groups to Keycloak groups automatically.


Step 6: Authentication Flows and MFA

Browser Flow Customization

AuthenticationFlowsbrowserDuplicate to create a custom flow. Add OTP Form as REQUIRED after username/password to enforce TOTP for all users.

WebAuthn / Passkeys

Keycloak 22+ supports WebAuthn out of the box:

  1. AuthenticationPoliciesWebAuthn Policy — set Relying Party ID to your domain.
  2. Add WebAuthn Authenticator to your browser flow.
  3. Users register their passkey on first login — no TOTP app needed.

Required Actions

Under Users → select user → Required User Actions, you can force:

  • CONFIGURE_TOTP — forces OTP setup on next login.
  • UPDATE_PASSWORD — forces password reset.
  • VERIFY_EMAIL — sends verification email.

Fine-Grained Authorization (UMA 2.0)

For resource-level permissions, enable Authorization on a confidential client:

  • Resources — define protected resources (e.g., /api/reports).
  • Scopes — define actions (read, write, delete).
  • Policies — rules based on roles, groups, JS scripts, or time.
  • Permissions — bind resources+scopes to policies.

Clients query the Token Endpoint with grant_type=urn:ietf:params:oauth:grant-type:uma-ticket to get a Requesting Party Token (RPT) with permission claims.


High Availability Setup

For production clusters, run 2+ Keycloak nodes sharing PostgreSQL and an Infinispan distributed cache:

environment:
  KC_CACHE: ispn
  KC_CACHE_STACK: jdbc-ping   # auto-discovery via DB
  KC_DB: postgres
  KC_DB_URL: jdbc:postgresql://pgpool/keycloak  # pgBouncer/pgPool

Load balancer requirements:

  • Enable sticky sessions (route by session cookie AUTH_SESSION_ID).
  • Health check endpoint: GET /health/ready (returns 200 when node is ready).
  • Drain connections gracefully — Keycloak replicates sessions across nodes.

Gotchas and Edge Cases

IssueCauseFix
Tokens too largeToo many roles/groups in tokenUse roles claim selectively; filter with mappers
CORS errorsWeb origins not setAdd exact origin in client Web Origins field
LDAP sync failsBind DN lacks read permissionsGrant Read Members of a Group in AD
Redirect URI mismatchTrailing slash or query stringKeycloak does exact match — add all URI variants
Clock skew errorsServer time driftSync NTP on all Keycloak and app nodes
Admin console 403Wrong realmEnsure you’re logged into master realm for realm management

Keycloak vs Alternatives

FeatureKeycloakAuthentikAutheliaAuth0OktaFusionAuth
Open-sourceYesYesYesNoNoPartial
Self-hostedYesYesYesNoNoYes
SAML 2.0YesYesNoYesYesYes
LDAP federationYesYesNoYesYesYes
Fine-grained authzYes (UMA)NoNoYes (paid)Yes (paid)Partial
ComplexityHighMediumLowLowLowMedium
Best forEnterprise SSOModern web appsReverse proxy MFASaaS/startupEnterpriseMid-market

Troubleshooting

ProblemSolution
”Invalid redirect_uri”Check client’s Valid Redirect URIs — must be exact match including trailing slash
”Client secret not valid”Regenerate secret in Clients → Credentials → Regenerate
Login loopCheck that KC_HOSTNAME matches the actual URL; proxy header misconfiguration
Email not sendingVerify SMTP config in Realm Settings → Email → Test Connection
Users not synced from LDAPCheck bind credentials; run Synchronize Changed Users from User Federation

Summary

  • Realms isolate tenants; always use a dedicated realm for applications.
  • Confidential clients use client secrets; public clients use PKCE.
  • Groups with role mappings scale RBAC — avoid assigning roles directly to users.
  • LDAP sync with FORCE mode keeps AD attributes authoritative.
  • For HA, use KC_CACHE=ispn with jdbc-ping — no ZooKeeper or Kubernetes required.
  • Automate everything via kcadm.sh or the Admin REST API for GitOps workflows.