A Kubernetes operator for managing Keycloak instances, realms, and OAuth2/OIDC clients declaratively with full GitOps compatibility.
Get a complete Keycloak setup running in under 10 minutes.
Helm charts are the recommended deployment path. Direct CR manifests are supported for advanced/manual workflows where you want to manage RBAC, secret access, and lifecycle wiring yourself.
# 1. Install the operator and a managed Keycloak instance
# Note: The chart creates the namespace by default, don't use --create-namespace
helm install keycloak-operator \
oci://ghcr.io/vriesdemichael/charts/keycloak-operator \
--namespace keycloak-system \
--set keycloak.managed=true \
--set keycloak.database.cnpg.enabled=true
# Or install from local charts:
# helm install keycloak-operator ./charts/keycloak-operator \
# --namespace keycloak-system
# 2. Create an identity realm
helm install my-app-realm \
oci://ghcr.io/vriesdemichael/charts/keycloak-realm \
--namespace my-app \
--create-namespace \
--set realmName=my-app \
--set operatorRef.namespace=keycloak-system \
--set 'clientAuthorizationGrants={my-app}'
# 3. Create an OAuth2 client
helm install my-app-client \
oci://ghcr.io/vriesdemichael/charts/keycloak-client \
--namespace my-app \
--set clientId=my-app \
--set realmRef.name=my-app-realm \
--set realmRef.namespace=my-app \
--set 'redirectUris={https://my-app.example.com/callback}'π Full Quick Start Guide β
Advanced: If you want to work directly with CR manifests instead of Helm releases, see Helm vs Direct CR Deployments.
- Declarative Configuration - Manage Keycloak entirely through Kubernetes resources
- Admission Webhooks - Immediate validation feedback with clear error messages (docs)
- GitOps Ready - Full observability with status conditions and
observedGenerationtracking - Drift Detection - Automatic detection of orphaned resources and configuration drift (docs)
- Cross-Namespace Support - Secure delegation model for multi-tenant environments
- Production Ready - Rate limiting, exponential backoff, and comprehensive monitoring
- Comprehensive Test Coverage - Unit and integration tests with coverage tracking
- Resource Quotas - Namespace-level limits on realms and clients via admission webhooks
- Rate Limiting - Two-level throttling (global + per-namespace) protects Keycloak from overload
- High Availability - Multi-replica Keycloak with PostgreSQL clustering via CloudNativePG
- OAuth2/OIDC Clients - Automated client provisioning with credential management
- Service Accounts - Declarative role assignment for machine-to-machine authentication
- OIDC Endpoint Discovery - Automatic population of all OIDC/OAuth2 endpoints in realm status
- Multi-Version Support - Supports Keycloak 24.x, 25.x, and 26.x via compatibility adapters
π Full Documentation - Versioned documentation with version selector
- Quick Start Guide - Get started in 10 minutes
- Helm vs Direct CR Deployments - Recommended workflow versus advanced manual path
- Architecture - How the operator works
- Admission Webhooks - Resource validation and quotas
- Security Model - Secret-based authorization explained
- Drift Detection - Orphan detection and auto-remediation
- Observability - Metrics, logs, and status conditions
- Versioning - How to access older documentation and chart versions
- Development Guide - Contributing to the project
Note: Version-Specific Documentation Use the version selector in the documentation to view docs for your installed version. See the Versioning Guide for details.
The operator manages three custom resources:
flowchart LR
kc[Keycloak\nInstance]
realm[KeycloakRealm\nIdentity Boundary]
client[KeycloakClient\nOAuth2/OIDC Boundary]
kc --> realm --> client
- Keycloak: The identity server instance with database and networking
- KeycloakRealm: Identity domain with users, roles, and authentication settings
- KeycloakClient: OAuth2/OIDC applications with automated credential management
The operator can manage resources (realms, clients) in an existing, external Keycloak instance instead of deploying its own.
In your values.yaml:
keycloak:
managed: false
url: "https://keycloak.example.com"
adminUsername: "admin"
adminSecret: "my-external-secret" # Secret in the operator namespace
adminPasswordKey: "password"This is the actual chart contract for external mode:
keycloak.managed=falsedisables templating of the managedKeycloakCRkeycloak.urltells the operator where the existing Keycloak instance liveskeycloak.adminSecrettells the operator which Secret to read for the admin passwordkeycloak.adminPasswordKeydefaults topassword, but can be overridden when your Secret uses a different key
Do not confuse this with keycloak.admin.existingSecret, which is only used for managed mode when keycloak.managed=true.
The admin secret must exist in the operator's namespace. It only needs to contain the password value the operator will use together with keycloak.adminUsername.
kubectl create secret generic my-external-secret \
--from-literal=password='your-admin-password' \
--namespace keycloak-systemNote: In external mode, the operator connects directly to the existing Keycloak instance using keycloak.url and the configured admin Secret. You typically do not deploy a managed Keycloak CR from this chart in that setup.
Create a complete OAuth2 setup:
# yaml-language-server: $schema=https://vriesdemichael.github.io/keycloak-operator/schemas/v1/Keycloak.json
# Keycloak instance with PostgreSQL database
apiVersion: vriesdemichael.github.io/v1
kind: Keycloak
metadata:
name: keycloak
namespace: keycloak-system
spec:
replicas: 3
image: quay.io/keycloak/keycloak:26.0.0
database:
type: postgresql
host: keycloak-postgres-rw
port: 5432
database: app
username: app
passwordSecret:
name: keycloak-postgres-app
key: password
---
# yaml-language-server: $schema=https://vriesdemichael.github.io/keycloak-operator/schemas/v1/KeycloakRealm.json
# Identity realm with client authorization grants
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakRealm
metadata:
name: my-app-realm
namespace: my-app
spec:
realmName: my-app
operatorRef:
namespace: keycloak-system
# Namespaces authorized to create clients in this realm
clientAuthorizationGrants:
- my-app
---
# yaml-language-server: $schema=https://vriesdemichael.github.io/keycloak-operator/schemas/v1/KeycloakClient.json
# OAuth2 client (namespace must be in realm's clientAuthorizationGrants)
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakClient
metadata:
name: my-app-client
namespace: my-app
spec:
clientId: my-app
realmRef:
name: my-app-realm
namespace: my-app
publicClient: false
redirectUris:
- "https://my-app.example.com/callback"See examples/ for advanced raw-manifest examples. For normal installs, prefer the Helm charts and the quick start flow above.
Get autocompletion, validation, and inline documentation in your IDE using published JSON schemas:
# yaml-language-server: $schema=https://vriesdemichael.github.io/keycloak-operator/schemas/v1/KeycloakRealm.json
apiVersion: vriesdemichael.github.io/v1
kind: KeycloakRealm
# ... IDE will autocomplete fields with descriptions!Features:
- β Autocomplete for all CRD fields
- β Inline validation with error messages
- β Field descriptions from CRD schema
- β Enum value suggestions
Supported IDEs:
- VS Code (with YAML extension)
- IntelliJ IDEA / PyCharm (built-in)
- Neovim (with yaml-language-server)
Available schemas:
https://vriesdemichael.github.io/keycloak-operator/schemas/v1/Keycloak.jsonhttps://vriesdemichael.github.io/keycloak-operator/schemas/v1/KeycloakRealm.jsonhttps://vriesdemichael.github.io/keycloak-operator/schemas/v1/KeycloakClient.json
Add the schema annotation as the first line of your YAML files to enable IDE features.
The operator uses a namespace grant authorization model combining Kubernetes RBAC with declarative access control:
- Realm Creation: Controlled by standard Kubernetes RBAC (who can create
KeycloakRealmresources) - Client Creation: Controlled by realm's
clientAuthorizationGrantslist (which namespaces can create clients) - Self-service: Teams can create realms and clients without platform team intervention
- GitOps Native: All authorization is declarative and stored in Git
- Auditability: All access changes tracked in Git history and Kubernetes audit logs
Read the Security Model documentation for detailed authorization architecture.
The operator exposes Prometheus metrics and includes a Grafana dashboard:
# Enable monitoring in Helm chart
helm install keycloak-operator ./charts/keycloak-operator \
--set monitoring.enabled=true \
--set monitoring.prometheusRules.enabled=true \
--set monitoring.grafanaDashboard.enabled=trueKey metrics:
- Reconciliation success/failure rates
- Rate limiting wait times and timeouts
- Reconciliation duration (p50/p95/p99)
- Resource counts by phase
See Observability for full details.
The operator implements two-level rate limiting to protect Keycloak from API overload:
env:
# Global rate limit (all namespaces combined)
- name: KEYCLOAK_API_GLOBAL_RATE_LIMIT_TPS
value: "50" # requests per second
- name: KEYCLOAK_API_GLOBAL_BURST
value: "100" # burst capacity
# Per-namespace rate limit (fair sharing)
- name: KEYCLOAK_API_NAMESPACE_RATE_LIMIT_TPS
value: "5" # requests per second
- name: KEYCLOAK_API_NAMESPACE_BURST
value: "10" # burst capacity
# Jitter to prevent thundering herd
- name: RECONCILE_JITTER_MAX_SECONDS
value: "5.0" # 0-5 second random delay| Scenario | Protection |
|---|---|
| Spam 1000 realms in one namespace | Limited to 5 req/s = 200s minimum |
| Multiple teams overwhelming Keycloak | Global 50 req/s enforced |
| Operator restart (50+ resources) | Jitter + rate limiting prevents flood |
Prometheus metrics available at :8081/metrics:
keycloak_operator_api_rate_limit_wait_seconds- Time waiting for tokenskeycloak_operator_api_rate_limit_acquired_total- Successful token acquisitionskeycloak_operator_api_rate_limit_timeouts_total- Rate limit timeout errorskeycloak_operator_api_rate_limit_budget_available- Current available tokens per namespace
Contributions welcome!
To set up a development environment:
# Clone the repository
git clone https://github.com/vriesdemichael/keycloak-operator.git
cd keycloak-operator
# Check required tools and install pre-commit hooks
task dev:setup
# Run quality checks
task quality:check
# Run unit tests
task test:unitSee Development Guide and AGENTS.md for more details.
MIT License - see LICENSE for details.