Keycloak Demystified: One Login to Rule Every App
An introduction to HashiCorp-grade identity management with Keycloak — core concepts, architecture, and a practical guide to deploying it with Docker and Kubernetes, including persistent themes/providers and MySQL-backed production setups.
If you've ever built a login system from scratch — password hashing, session tokens, "forgot password" flows, social login buttons — you know it's a rabbit hole. Keycloak exists so you never have to go down it again.
What is Keycloak?
Keycloak is an open-source Identity and Access Management (IAM) platform, originally built by Red Hat (now a CNCF project). It handles authentication, authorization, and single sign-on (SSO) for web and mobile applications, so development teams don't have to reinvent user management for every app they ship.
Core Capabilities
| Feature | What it does |
Single Sign-On (SSO) | Log in once, access every connected app — no repeated credential prompts.
Authentication | Username/password, social logins (Google, Facebook), MFA, OpenID Connect, SAML.
Authorization | Fine-grained, role- and attribute-based access control per resource.
User Federation | Syncs with LDAP, Active Directory, or custom user stores.
Identity Brokering | Lets users log in via existing social or enterprise accounts.
Self-Service | Users manage their own profiles and password resets.
Security Hardening | Session management, token validation, brute-force protection.
Extensibility | Fully open source and customizable for enterprise needs.
Because it speaks standard protocols (OpenID Connect, SAML, OAuth2), Keycloak drops into almost any stack — regardless of language or framework — as a centralized identity layer.
Key Concepts
Before deploying, it helps to know the vocabulary:
- Realm — An isolated security domain. Each realm has its own users, groups, roles, and clients, so you can separate tenants or environments cleanly.
- Clients — The applications or services that authenticate against Keycloak (web apps, mobile apps, APIs).
- Client Scopes — Named sets of permissions/attributes a client can request during login (e.g., an
emailscope). - Roles — Fine-grained permissions assigned to users or clients, defining what they're allowed to do.
- Identity Providers — External login sources (Google, GitHub, corporate SSO) federated into a realm.
- User Federation — Bridges to external directories like LDAP or Active Directory.
- Authentication Flows — Configurable login pipelines supporting MFA and custom logic.
Architecture at a Glance
User → Application (Client) → Keycloak Realm → Auth Method (local / LDAP / social IdP)
↓
Token issued (OIDC / SAML)
↓
Application validates token → Access granted
Keycloak sits in front of your applications as a dedicated auth layer — apps redirect to Keycloak for login, and get back a signed token they can trust.
Deployment: Docker (Quick Start)
For local testing or demos, a single container gets you running in seconds:
docker run -d \
--name keycloak \
-p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=12345Q \
quay.io/keycloak/keycloak:latest start-dev
start-dev runs Keycloak in development mode — fine for experimenting, not for production (no HTTPS enforcement, in-memory-friendly defaults).
Deployment: Kubernetes (Dev Mode)
For a quick in-cluster deployment, define a Deployment and expose it via NodePort.
keycloak-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:22.0.3
args: ["start-dev"]
env:
- name: KEYCLOAK_ADMIN
value: "admin"
- name: KEYCLOAK_ADMIN_PASSWORD
value: "admin"
- name: KC_PROXY
value: "edge"
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /realms/master
port: 8080
keycloak-service.yaml
apiVersion: v1
kind: Service
metadata:
name: keycloak
labels:
app: keycloak
spec:
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 31503
selector:
app: keycloak
type: NodePort
kubectl apply -f keycloak-deployment.yaml
kubectl apply -f keycloak-service.yaml
Or skip the YAML entirely and apply the official quickstart directly:
kubectl create -f https://raw.githubusercontent.com/keycloak/keycloak-quickstarts/latest/kubernetes/keycloak.yaml
Production-Grade Kubernetes Deployment
A production setup needs three things dev mode skips: persistent storage for themes/providers, a real database instead of embedded storage, and proper hostname/proxy configuration.
1. Persistent Volumes for Themes and Providers
Custom themes and provider JARs shouldn't vanish on pod restart. Provision dedicated PVs/PVCs for each:
keycloak-pv-providers.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: keycloak-pv-providers
labels:
type: local
spec:
storageClassName: local-storage-class
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/app/data/keycloak/providers"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: keycloak-pvc-providers
spec:
storageClassName: local-storage-class
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
keycloak-pv-themes.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: keycloak-pv
labels:
type: local
spec:
storageClassName: local-storage-class
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/app/data/keycloak/themes"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: keycloak-pv-claim
spec:
storageClassName: local-storage-class
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
2. Deployment with MySQL Backend and Reverse Proxy Support
This is the setup you'd actually run behind an ingress/reverse proxy, with a real hostname and a MySQL-backed realm store — plus Prometheus metrics enabled.
keycloak-deploy-all.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
spec:
selector:
matchLabels:
app: keycloak
replicas: 1
template:
metadata:
annotations:
kubernetes.io/change-cause: customize keycloak of quay.io/keycloak/keycloak:25.0.2 for enable monitoring
labels:
app: keycloak
spec:
volumes:
- name: keycloak-pv
persistentVolumeClaim:
claimName: keycloak-pv-claim
- name: keycloak-pv-providers
persistentVolumeClaim:
claimName: keycloak-pvc-providers
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:latest
args: ["start", "--metrics-enabled=true"]
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: "/opt/keycloak/themes"
name: keycloak-pv
- mountPath: "/opt/keycloak/providers"
name: keycloak-pv-providers
env:
- name: KEYCLOAK_ADMIN
value: 'admin'
- name: KEYCLOAK_ADMIN_PASSWORD
value: '12345Q'
- name: PROXY_ADDRESS_FORWARDING
value: 'true'
- name: KC_HOSTNAME
value: 'your-domain.example.com'
- name: KC_HTTP_RELATIVE_PATH
value: '/idp'
- name: KC_HOSTNAME_ADMIN_URL
value: 'https://your-domain.example.com/idp'
- name: KC_HTTP_HOST
value: '0.0.0.0'
- name: KC_PROXY
value: 'edge'
- name: KC_HOSTNAME_STRICT
value: 'false'
- name: KC_DB
value: 'mysql'
- name: KC_DB_URL
value: 'jdbc:mysql://<db-host>:3306/keycloak'
- name: KC_DB_USERNAME
value: 'root'
- name: KC_DB_PASSWORD
value: '<db-password>'
ports:
- name: idp-cport
containerPort: 8080
- name: idp-mport
containerPort: 9000
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: keycloak
spec:
selector:
app: keycloak
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 30500
- name: management-port
port: 9000
targetPort: 9000
nodePort: 30501
type: NodePort
kubectl apply -f keycloak-pv-providers.yaml
kubectl apply -f keycloak-pv-themes.yaml
kubectl apply -f keycloak-deploy-all.yaml
Note:
--metrics-enabled=trueexposes Prometheus-scrapable metrics on the management port (9000), useful for wiring into a Grafana dashboard alongside the rest of your platform observability stack. Replace database credentials and hostnames with values pulled from Vault/ESO rather than hardcoding them in the manifest.
Where Keycloak Fits in a GitOps Platform
For teams already running ArgoCD, Vault/ESO, and Helm, Keycloak slots in naturally:
- Package the production manifests above into a Helm chart, templating hostname, DB credentials, and replica count.
- Store DB credentials and admin passwords in Vault, synced via ExternalSecrets rather than plaintext env vars.
- Let ArgoCD manage the Keycloak Application, so realm/theme/provider changes ship through the same GitOps pipeline as everything else.
- Front it with Traefik (Gateway API / HTTPRoute) for TLS termination and routing, matching the
KC_PROXY=edgeconfiguration.
Summary
| Component | Role | |---|---| | Realm | Isolated tenant/security boundary | | Client | Application registered to authenticate via Keycloak | | Identity Provider | External login source (social/enterprise) | | User Federation | Bridge to LDAP/AD user stores | | PV/PVC (themes, providers) | Persist customizations across restarts | | MySQL Backend | Durable storage for realms, users, and configuration |
Auth flow: User → App redirects to Keycloak → Keycloak authenticates → Token issued → App grants access