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:
| Concept | Description |
|---|---|
| Realm | Isolated tenant. Owns users, clients, roles, groups, IdPs |
| Client | Application registered to use Keycloak for auth |
| User | Identity within the realm (local or federated) |
| Role | Permission label. Realm roles span all clients; client roles are scoped |
| Group | Collection of users with inherited role assignments |
| Identity Provider | External IdP (Google, GitHub, SAML, LDAP) |
| Authentication Flow | Configurable 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:
- Click the realm dropdown (top-left) → Create Realm.
- Set a name (e.g.,
myapp), enable it, click Create.
Key realm settings to configure:
| Setting | Recommended value |
|---|---|
| Display name | Your brand name |
| Login theme | keycloak (or custom) |
| Require SSL | external requests (production) |
| SSO Session Idle | 30 minutes |
| Access Token Lifespan | 5 minutes |
| Refresh Token Lifespan | 30 minutes |
Configure SMTP under Realm Settings → Email for password reset and verification emails.
Step 3: OpenID Connect Client Configuration
Go to Clients → Create 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
| Feature | Confidential | Public |
|---|---|---|
| Has client secret | Yes | No |
| Use case | Server-side (Node, .NET, Python) | SPA, mobile app |
| Auth code flow | With client secret | With PKCE |
| Risk if compromised | High — rotate secret | Low — secret-less |
Protocol Mappers
Add mappers to include custom claims in tokens: Clients → your client → Client Scopes → Add 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
- Realm → Groups → Create group → name it
Administrators. - Open the group → Role Mapping → Assign role → select
app-admin. - 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 Providers → Add provider → select Google:
- Create an OAuth 2.0 app in Google Cloud Console.
- Set Client ID and Client Secret from Google.
- Set Redirect URI back to Keycloak:
https://auth.example.com/realms/myapp/broker/google/endpoint. - Enable Trust Email if Google-verified emails should be auto-trusted.
LDAP / Active Directory Federation
User Federation → Add provider → LDAP:
| Setting | Value |
|---|---|
| Vendor | Active Directory |
| Connection URL | ldap://ad.corp.example.com |
| Bind DN | CN=svc-keycloak,OU=ServiceAccounts,DC=corp,DC=example,DC=com |
| Users DN | OU=Users,DC=corp,DC=example,DC=com |
| Sync Mode | FORCE (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
Authentication → Flows → browser → Duplicate 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:
- Authentication → Policies → WebAuthn Policy — set
Relying Party IDto your domain. - Add WebAuthn Authenticator to your browser flow.
- 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
| Issue | Cause | Fix |
|---|---|---|
| Tokens too large | Too many roles/groups in token | Use roles claim selectively; filter with mappers |
| CORS errors | Web origins not set | Add exact origin in client Web Origins field |
| LDAP sync fails | Bind DN lacks read permissions | Grant Read Members of a Group in AD |
| Redirect URI mismatch | Trailing slash or query string | Keycloak does exact match — add all URI variants |
| Clock skew errors | Server time drift | Sync NTP on all Keycloak and app nodes |
| Admin console 403 | Wrong realm | Ensure you’re logged into master realm for realm management |
Keycloak vs Alternatives
| Feature | Keycloak | Authentik | Authelia | Auth0 | Okta | FusionAuth |
|---|---|---|---|---|---|---|
| Open-source | Yes | Yes | Yes | No | No | Partial |
| Self-hosted | Yes | Yes | Yes | No | No | Yes |
| SAML 2.0 | Yes | Yes | No | Yes | Yes | Yes |
| LDAP federation | Yes | Yes | No | Yes | Yes | Yes |
| Fine-grained authz | Yes (UMA) | No | No | Yes (paid) | Yes (paid) | Partial |
| Complexity | High | Medium | Low | Low | Low | Medium |
| Best for | Enterprise SSO | Modern web apps | Reverse proxy MFA | SaaS/startup | Enterprise | Mid-market |
Troubleshooting
| Problem | Solution |
|---|---|
| ”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 loop | Check that KC_HOSTNAME matches the actual URL; proxy header misconfiguration |
| Email not sending | Verify SMTP config in Realm Settings → Email → Test Connection |
| Users not synced from LDAP | Check 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
FORCEmode keeps AD attributes authoritative. - For HA, use
KC_CACHE=ispnwithjdbc-ping— no ZooKeeper or Kubernetes required. - Automate everything via
kcadm.shor the Admin REST API for GitOps workflows.