Keycloak Cheat Sheet
Overview
Keycloak is a feature-rich, open-source Identity and Access Management solution developed by Red Hat and donated to the Cloud Native Computing Foundation (CNCF) as part of the CNCF Sandbox. It provides single sign-on (SSO) capabilities, centralized user management, and authentication/authorization services that can be integrated with virtually any application. Keycloak eliminates the need for individual applications to implement their own login, registration, and security features by centralizing these concerns in a dedicated IAM platform.
Keycloak’s architecture is organized around the concept of “Realms” — isolated identity domains, each with its own users, clients, roles, and configuration. A single Keycloak instance can host multiple realms, allowing you to manage completely separate user bases (e.g., internal employees, external partners, and end customers) without interference. Within a realm, “Clients” represent applications that delegate authentication to Keycloak. Each client can be configured for different protocols (OIDC, SAML), grant types, redirect URIs, and scopes.
The platform supports standard protocols including OpenID Connect (OIDC), OAuth 2.0, and SAML 2.0, making it compatible with virtually any modern application stack. User Federation allows Keycloak to connect to external user directories like LDAP and Active Directory, synchronizing users without requiring migration. Multi-factor authentication, social login (Google, GitHub, Facebook, etc.), brute force detection, and password policies are all built in. For enterprise environments, Keycloak supports high availability clustering via Infinispan (JGroups) and can be backed by PostgreSQL, MySQL, or other JDBC-compatible databases.
Installation
Docker (Quickstart)
# Development mode (no TLS, ephemeral storage)
docker run -d \
--name keycloak \
-p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:latest \
start-dev
# Access at http://localhost:8080/admin
Docker (Production Mode)
docker run -d \
--name keycloak \
-p 8443:8443 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=changeme \
-e KC_DB=postgres \
-e KC_DB_URL=jdbc:postgresql://db:5432/keycloak \
-e KC_DB_USERNAME=keycloak \
-e KC_DB_PASSWORD=dbpassword \
-e KC_HOSTNAME=auth.example.com \
-e KC_HTTPS_CERTIFICATE_FILE=/certs/tls.crt \
-e KC_HTTPS_CERTIFICATE_KEY_FILE=/certs/tls.key \
-v /etc/ssl/keycloak:/certs:ro \
quay.io/keycloak/keycloak:latest \
start
Docker Compose with PostgreSQL
version: "3.9"
services:
postgres:
image: postgres:16
container_name: keycloak-db
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${KC_DB_PASSWORD:-securepassword}
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 10s
timeout: 5s
retries: 5
keycloak:
image: quay.io/keycloak/keycloak:latest
container_name: keycloak
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${KC_DB_PASSWORD:-securepassword}
KC_HOSTNAME: ${KC_HOSTNAME:-auth.example.com}
KC_HOSTNAME_STRICT: "true"
KC_HTTP_ENABLED: "true"
KC_PROXY: edge
KEYCLOAK_ADMIN: ${KC_ADMIN:-admin}
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD:-changeme}
command: start --optimized
ports:
- "8080:8080"
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
volumes:
postgres_data:
Kubernetes (Helm)
# Add Bitnami Helm repo
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
# Install Keycloak with PostgreSQL
helm install keycloak bitnami/keycloak \
--set auth.adminUser=admin \
--set auth.adminPassword=securepassword \
--set postgresql.enabled=true \
--set postgresql.auth.password=dbpassword \
--set ingress.enabled=true \
--set ingress.hostname=auth.example.com \
--namespace keycloak --create-namespace
# Check status
kubectl get pods -n keycloak
kubectl get svc -n keycloak
Standalone on Linux (from ZIP)
# Download and extract
curl -OL https://github.com/keycloak/keycloak/releases/download/24.0.0/keycloak-24.0.0.zip
unzip keycloak-24.0.0.zip
cd keycloak-24.0.0
# Configure database
echo 'db=postgres
db-url=jdbc:postgresql://localhost:5432/keycloak
db-username=keycloak
db-password=securepassword
hostname=auth.example.com' >> conf/keycloak.conf
# Start production server
export KEYCLOAK_ADMIN=admin
export KEYCLOAK_ADMIN_PASSWORD=changeme
bin/kc.sh build
bin/kc.sh start
Configuration
Key Environment Variables
# Core configuration
KC_DB=postgres # Database type: postgres, mysql, mariadb, mssql
KC_DB_URL=jdbc:postgresql://... # JDBC URL
KC_DB_USERNAME=keycloak
KC_DB_PASSWORD=secret
# Hostname
KC_HOSTNAME=auth.example.com # Public hostname
KC_HOSTNAME_STRICT=true # Reject requests to other hostnames
KC_HTTP_ENABLED=true # Allow HTTP (needed behind reverse proxy)
KC_PROXY=edge # Proxy mode: edge, reencrypt, passthrough
# TLS
KC_HTTPS_CERTIFICATE_FILE=/path/tls.crt
KC_HTTPS_CERTIFICATE_KEY_FILE=/path/tls.key
# Admin
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=changeme
# Performance
KC_HTTP_MAX_QUEUED_REQUESTS=1000
KC_CACHE=ispn # Infinispan clustering
KC_CACHE_STACK=kubernetes # For K8s cluster discovery
Reverse Proxy (Nginx/Caddy) Setup
# Nginx config for Keycloak behind proxy
server {
listen 443 ssl;
server_name auth.example.com;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
}
}
# Required Keycloak settings when behind a proxy
KC_PROXY=edge
KC_HOSTNAME=auth.example.com
KC_HTTP_ENABLED=true
Core Commands
| Command | Description |
|---|---|
bin/kc.sh start-dev | Start in development mode (no TLS required) |
bin/kc.sh start | Start in production mode |
bin/kc.sh start --optimized | Start with pre-built image (faster) |
bin/kc.sh build | Build optimized server image |
bin/kc.sh export --dir /tmp/export | Export realm configuration to files |
bin/kc.sh import --dir /tmp/export | Import realm configuration from files |
bin/kc.sh show-config | Display effective configuration |
bin/kcadm.sh config credentials ... | Authenticate kcadm CLI |
bin/kcadm.sh get realms | List all realms |
bin/kcadm.sh create realms -s realm=NAME | Create a new realm |
bin/kcadm.sh get users -r REALM | List users in a realm |
bin/kcadm.sh create users -r REALM | Create a user |
bin/kcadm.sh get clients -r REALM | List clients in a realm |
bin/kcadm.sh create clients -r REALM | Create a client |
bin/kcadm.sh get groups -r REALM | List groups |
bin/kcadm.sh get roles -r REALM | List roles |
bin/kcadm.sh delete users/USER_ID -r REALM | Delete a user |
Advanced Usage
kcadm.sh CLI Administration
# Authenticate (get admin token)
bin/kcadm.sh config credentials \
--server http://localhost:8080 \
--realm master \
--user admin \
--password changeme
# Create a realm
bin/kcadm.sh create realms \
-s realm=myrealm \
-s enabled=true \
-s displayName="My Application Realm"
# Create a user
bin/kcadm.sh create users -r myrealm \
-s username=john.doe \
-s email=john@example.com \
-s firstName=John \
-s lastName=Doe \
-s enabled=true
# Set user password
bin/kcadm.sh set-password -r myrealm \
--username john.doe \
--new-password secretpassword \
--temporary false
# Create a client (OIDC, confidential)
bin/kcadm.sh create clients -r myrealm \
-s clientId=my-app \
-s name="My Application" \
-s enabled=true \
-s clientAuthenticatorType=client-secret \
-s 'redirectUris=["https://app.example.com/*"]' \
-s 'webOrigins=["https://app.example.com"]' \
-s protocol=openid-connect
# Get client secret
CLIENT_ID=$(bin/kcadm.sh get clients -r myrealm -q clientId=my-app | jq -r '.[0].id')
bin/kcadm.sh get clients/$CLIENT_ID/client-secret -r myrealm
# Create a role
bin/kcadm.sh create roles -r myrealm -s name=admin -s description="Admin role"
# Assign role to user
USER_ID=$(bin/kcadm.sh get users -r myrealm -q username=john.doe | jq -r '.[0].id')
ROLE_ID=$(bin/kcadm.sh get roles -r myrealm -q name=admin | jq -r '.[0].id')
bin/kcadm.sh add-roles -r myrealm --uusername john.doe --rolename admin
OIDC Token Flows via REST API
# Authorization Code flow — get auth URL
# Browser redirects user to:
# https://auth.example.com/realms/myrealm/protocol/openid-connect/auth
# ?client_id=my-app
# &redirect_uri=https://app.example.com/callback
# &response_type=code
# &scope=openid email profile
# Exchange authorization code for tokens
curl -X POST https://auth.example.com/realms/myrealm/protocol/openid-connect/token \
-d "grant_type=authorization_code" \
-d "client_id=my-app" \
-d "client_secret=CLIENT_SECRET" \
-d "code=AUTH_CODE" \
-d "redirect_uri=https://app.example.com/callback"
# Client credentials flow (machine-to-machine)
curl -X POST https://auth.example.com/realms/myrealm/protocol/openid-connect/token \
-d "grant_type=client_credentials" \
-d "client_id=service-account" \
-d "client_secret=SECRET"
# Refresh access token
curl -X POST https://auth.example.com/realms/myrealm/protocol/openid-connect/token \
-d "grant_type=refresh_token" \
-d "client_id=my-app" \
-d "client_secret=SECRET" \
-d "refresh_token=REFRESH_TOKEN"
# Introspect a token
curl -X POST https://auth.example.com/realms/myrealm/protocol/openid-connect/token/introspect \
-u "my-app:SECRET" \
-d "token=ACCESS_TOKEN"
# Get OIDC discovery document
curl https://auth.example.com/realms/myrealm/.well-known/openid-configuration
LDAP/Active Directory Federation
# Create LDAP user federation via API
bin/kcadm.sh create components -r myrealm \
-s name="Corporate LDAP" \
-s providerId=ldap \
-s providerType=org.keycloak.storage.UserStorageProvider \
-s 'config.vendor=["ad"]' \
-s 'config.connectionUrl=["ldap://ad.corp.example.com:389"]' \
-s 'config.bindDn=["CN=keycloak-svc,OU=ServiceAccounts,DC=corp,DC=example,DC=com"]' \
-s 'config.bindCredential=["ldappassword"]' \
-s 'config.usersDn=["OU=Users,DC=corp,DC=example,DC=com"]' \
-s 'config.userObjectClasses=["person,organizationalPerson,user"]' \
-s 'config.authType=["simple"]' \
-s 'config.syncRegistrations=["false"]' \
-s 'config.importEnabled=["true"]' \
-s 'config.editMode=["READ_ONLY"]' \
-s 'config.periodicFullSync=["true"]' \
-s 'config.fullSyncPeriod=["604800"]' \
-s 'config.periodicChangedUsersSync=["true"]' \
-s 'config.changedSyncPeriod=["86400"]'
# Trigger manual sync
COMPONENT_ID="<component-id-from-above>"
bin/kcadm.sh create components/$COMPONENT_ID/sync?action=triggerFullSync -r myrealm
Multi-Factor Authentication Configuration
# Require OTP for a realm (via CLI)
bin/kcadm.sh update realms/myrealm \
-s 'otpPolicyType=totp' \
-s 'otpPolicyAlgorithm=HmacSHA1' \
-s 'otpPolicyDigits=6' \
-s 'otpPolicyPeriod=30'
# Require MFA via authentication flow
# Navigate in Admin UI: Authentication > Flows > Browser
# Set "OTP Form" execution to "Required"
# Require MFA for specific group/role via Conditional OTP
# Admin UI: Authentication > Flows > Create a copy of Browser flow
# Add "Condition - User Role" as required
# Configure which role triggers MFA requirement
Export and Import Realms
# Export specific realm (running server)
bin/kc.sh export \
--dir /tmp/realm-export \
--realm myrealm \
--users realm_file
# Export all realms
bin/kc.sh export --dir /tmp/all-realms
# Import realm on startup
bin/kc.sh start-dev \
--import-realm \
--import-realm-file /tmp/realm-export/myrealm-realm.json
# Import via kcadm
bin/kcadm.sh create realms -f /tmp/realm-export/myrealm-realm.json
Custom Themes
# Theme directory structure
themes/
my-theme/
login/ # Login, registration, error pages
theme.properties
resources/
css/
custom.css
img/
logo.png
messages/
messages_en.properties
login.ftl # FreeMarker template override
account/ # User account console
email/ # Email templates
# theme.properties
parent=keycloak
import=common/keycloak
# Apply theme to realm
bin/kcadm.sh update realms/myrealm \
-s loginTheme=my-theme \
-s accountTheme=my-theme \
-s emailTheme=my-theme
Common Workflows
New Application Onboarding
# 1. Create realm (or use existing)
bin/kcadm.sh create realms -s realm=myapp -s enabled=true
# 2. Create client
bin/kcadm.sh create clients -r myapp \
-s clientId=my-web-app \
-s enabled=true \
-s protocol=openid-connect \
-s publicClient=false \
-s clientAuthenticatorType=client-secret \
-s 'redirectUris=["https://app.example.com/*"]' \
-s 'webOrigins=["+"]' \
-s standardFlowEnabled=true \
-s serviceAccountsEnabled=false
# 3. Get client secret
CLIENT_ID=$(bin/kcadm.sh get clients -r myapp -q clientId=my-web-app | jq -r '.[0].id')
bin/kcadm.sh get clients/$CLIENT_ID/client-secret -r myapp
# 4. Create roles
bin/kcadm.sh create roles -r myapp -s name=user
bin/kcadm.sh create roles -r myapp -s name=admin
# 5. Create test user
bin/kcadm.sh create users -r myapp -s username=testuser -s enabled=true
bin/kcadm.sh set-password -r myapp --username testuser --new-password test123
bin/kcadm.sh add-roles -r myapp --uusername testuser --rolename user
# 6. Test OIDC flow
curl -s -X POST \
"http://localhost:8080/realms/myapp/protocol/openid-connect/token" \
-d "grant_type=password&client_id=my-web-app&client_secret=SECRET&username=testuser&password=test123" \
| jq .access_token
Health and Monitoring
# Health endpoints
curl http://localhost:8080/health
curl http://localhost:8080/health/ready
curl http://localhost:8080/health/live
# Metrics endpoint (Prometheus format)
curl http://localhost:8080/metrics
# Admin REST API — get realm stats
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/admin/realms/myrealm/users/count
# Check active sessions
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/admin/realms/myrealm/sessions/stats
Tips and Best Practices
| Practice | Details |
|---|---|
| Always use HTTPS in production | Never expose Keycloak on plain HTTP in production — TLS is mandatory |
Use KC_PROXY=edge | Required when running behind nginx/Caddy to pass real IP and proto headers |
| PostgreSQL for production | SQLite/H2 (default in dev) is not supported in production — use PostgreSQL |
| Export realm config to Git | Version-control realm JSON exports for disaster recovery and auditability |
| Tune token lifetimes | Set short access token lifetimes (5-15 min) and longer refresh token lifetimes |
| Enable brute force protection | Admin UI: Realm Settings > Security Defenses > Brute Force Detection |
| Use groups for role assignment | Assign roles to groups, add users to groups — easier than per-user role assignment |
| Dedicated service accounts | Create separate clients with service accounts for M2M auth, never reuse user clients |
| Health check all nodes | Monitor /health/ready in load balancer health checks |
| Backup database regularly | Realm config, users, and sessions are all in the database |
| Client scope mapping | Use protocol mappers to include custom claims in tokens |
| Review event logs | Enable Admin Events and Login Events in Admin UI for audit trails |