KeycloakRealm
Identifier field: Set the realm name in the
spec.realmNamefield. It is required and immutable once set. Arealmkey insidespec.definitionis tolerated only when it matchesspec.realmName; a conflicting value is rejected.
spec.realmNameis immutable once set. A realm rename in Keycloak is destructive and would orphan the realm, so the value is locked after the first time it is set (the one-time migration from unset to a value is permitted).
A KeycloakRealm represents a realm within a Keycloak instance.
Specification
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakRealm
metadata:
name: my-realm
spec:
# One of instanceRef or clusterInstanceRef must be specified
# Option 1: Reference to a namespaced KeycloakInstance
instanceRef:
name: my-keycloak
# Option 2: Reference to a ClusterKeycloakInstance
# clusterInstanceRef:
# name: my-cluster-instance
# Required: Realm name in Keycloak (do NOT set realm in definition)
realmName: my-realm
# Required: Realm definition (Keycloak RealmRepresentation)
definition:
displayName: My Realm
enabled: true
# ... any other Keycloak realm properties
Status
status:
ready: true
status: "Ready"
message: "Realm synchronized successfully"
resourcePath: "/admin/realms/my-realm"
instance:
instanceRef: my-keycloak
conditions:
- type: Ready
status: "True"
reason: Synchronized
Example
Basic Realm
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakRealm
metadata:
name: my-app-realm
spec:
instanceRef:
name: production-keycloak
realmName: my-app
definition:
displayName: My Application
enabled: true
With ClusterKeycloakInstance
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakRealm
metadata:
name: my-app-realm
spec:
clusterInstanceRef:
name: central-keycloak
realmName: my-app
definition:
displayName: My Application
enabled: true
Full Configuration
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakRealm
metadata:
name: production-realm
spec:
instanceRef:
name: production-keycloak
realmName: production
definition:
displayName: Production Realm
enabled: true
# Login settings
registrationAllowed: false
registrationEmailAsUsername: true
loginWithEmailAllowed: true
duplicateEmailsAllowed: false
resetPasswordAllowed: true
rememberMe: true
# Session settings
ssoSessionIdleTimeout: 1800
ssoSessionMaxLifespan: 36000
accessTokenLifespan: 300
# Security settings
bruteForceProtected: true
permanentLockout: false
maxFailureWaitSeconds: 900
minimumQuickLoginWaitSeconds: 60
waitIncrementSeconds: 60
quickLoginCheckMilliSeconds: 1000
maxDeltaTimeSeconds: 43200
failureFactor: 5
# Themes
loginTheme: keycloak
accountTheme: keycloak
adminTheme: keycloak
emailTheme: keycloak
# SMTP settings (non-sensitive parts in definition)
smtpServer:
host: smtp.example.com
port: "587"
fromDisplayName: My App
from: noreply@example.com
starttls: "true"
auth: "true"
# SMTP credentials from a Kubernetes Secret (recommended over plaintext in definition)
smtpSecretRef:
name: my-smtp-credentials
userKey: user # optional, defaults to "user"
passwordKey: password # optional, defaults to "password"
SMTP Credentials from Secret
To avoid storing SMTP credentials in plaintext in the CR, use smtpSecretRef to reference a Kubernetes Secret:
kubectl create secret generic smtp-credentials \
--from-literal=user=smtp-user@example.com \
--from-literal=password=my-smtp-password
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakRealm
metadata:
name: my-realm
spec:
instanceRef:
name: my-keycloak
smtpSecretRef:
name: smtp-credentials
# userKey: user # default
# passwordKey: password # default
realmName: my-realm
definition:
enabled: true
smtpServer:
host: smtp.example.com
port: "587"
from: noreply@example.com
starttls: "true"
auth: "true"
The operator reads the user and password values from the referenced secret and injects them into smtpServer before sending the realm configuration to Keycloak. Always set from: Keycloak 26.8 and later reject a realm whose smtpServer has no sender address (Invalid sender address 'null'), which surfaces as CreateFailed or UpdateFailed on the realm. Also set auth: "true": Keycloak 26.8 keeps user and password on realm creation but drops them on every update when auth is not enabled, so the injected credentials would disappear on the operator’s next reconcile. The secret must exist in the same namespace as the KeycloakRealm.
For ClusterKeycloakRealm, the secret namespace must be specified explicitly:
apiVersion: keycloak.hostzero.com/v1beta1
kind: ClusterKeycloakRealm
metadata:
name: my-realm
spec:
clusterInstanceRef:
name: central-keycloak
smtpSecretRef:
name: smtp-credentials
namespace: keycloak-system
realmName: my-realm
definition:
enabled: true
smtpServer:
host: smtp.example.com
port: "587"
from: noreply@example.com
starttls: "true"
auth: "true"
When the referenced secret changes, the operator automatically re-reconciles the realm to pick up the new credentials.
Definition Properties
The definition field accepts any property from the Keycloak RealmRepresentation.
Common properties:
| Property | Type | Description |
|---|---|---|
realm | string | Realm name (required) |
displayName | string | Display name for the realm |
enabled | boolean | Whether the realm is enabled |
registrationAllowed | boolean | Allow user registration |
loginWithEmailAllowed | boolean | Allow login with email |
ssoSessionIdleTimeout | integer | SSO session idle timeout (seconds) |
accessTokenLifespan | integer | Access token lifespan (seconds) |
Nested Resources
definition holds the realm’s own attributes only. The collections Keycloak nests inside a RealmRepresentation each have a CRD of their own, and the realm rejects them with Ready=False and reason UnsupportedDefinitionField:
definition key | Use instead |
|---|---|
clients | KeycloakClient |
clientScopes | KeycloakClientScope |
users | KeycloakUser |
groups | KeycloakGroup |
roles | KeycloakRole |
components | KeycloakComponent |
authenticationFlows | KeycloakAuthenticationFlow |
identityProviders | KeycloakIdentityProvider |
identityProviderMappers | KeycloakIdentityProviderMapper |
requiredActions | KeycloakRequiredAction |
organizations | KeycloakOrganization |
Keycloak only imports these when a realm is created; GET /admin/realms/{realm} leaves them out and PUT ignores them. A realm declaring them would therefore never settle in drift detection and would re-send the realm on every reconcile while the nested entries never changed in Keycloak. See Spec Layout for the rule behind this. Flow bindings such as browserFlow are realm attributes and stay in definition; see Binding Custom Authentication Flows.
Partial Updates
definition is sent to Keycloak as-is. For an existing realm, Keycloak applies only the fields present in the representation and leaves everything else unchanged, so a minimal definition can adopt a pre-existing realm without rewriting it.
Keycloak makes an exception for the OAuth 2.0 device flow (oauth2DeviceCodeLifespan, oauth2DevicePollingInterval), CIBA and PAR settings (attributes.ciba*, attributes.parRequestUriLifespan) and browserSecurityHeaders: it resets these to defaults when they are omitted from an update. The operator compensates by copying the realm’s current values for these fields into the request when the definition does not set them.
Because of that, attributes behaves as a whole: when the definition sets attributes, Keycloak replaces the realm’s attribute map with it, dropping any attribute not listed.
Binding Custom Authentication Flows
A realm definition may bind built-in authentication points to custom flows via browserFlow, registrationFlow, directGrantFlow, resetCredentialsFlow, clientAuthenticationFlow, or dockerAuthenticationFlow. Keycloak rejects realm imports that reference a flow alias which does not yet exist (see keycloak/keycloak#23980), which would otherwise prevent declaratively creating the realm and the flow at the same time.
The operator works around this with deferred bindings:
- On the first
CreateRealmcall, any flow-binding fields whose target alias does not yet exist in Keycloak are stripped before the request is sent. The realm is created and markedReadyso that the flows can be created against it, but withstatus.status(and theReadycondition reason) set toFlowBindingsDeferredinstead ofReady. - The realm controller watches
KeycloakAuthenticationFlowresources and requeues the realm immediately when a referenced flow becomes ready, instead of waiting for the next periodic resync. - On the next reconcile (either triggered by the watch or by the periodic resync) the operator updates the realm with the original bindings now that the referenced flows exist, and the status reason returns to
Ready.
Practically this means you can apply a KeycloakRealm and its KeycloakAuthenticationFlow resources together — in any order — and convergence happens within seconds. To wait for the bindings to be applied rather than just for the realm to exist, wait on the status reason:
kubectl wait keycloakrealm/my-realm --for=jsonpath='{.status.status}'=Ready
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakRealm
metadata:
name: my-realm
spec:
instanceRef:
name: my-keycloak
realmName: my-realm
definition:
enabled: true
browserFlow: my-custom-browser # may be created later
registrationFlow: my-custom-registration # may be created later
---
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakAuthenticationFlow
metadata:
name: my-custom-browser
spec:
realmRef:
name: my-realm
alias: my-custom-browser
providerId: basic-flow
executions:
- authenticator: auth-cookie
requirement: ALTERNATIVE
See KeycloakAuthenticationFlow for the flow CRD reference.
Identity Providers and Mappers
Identity providers themselves can be embedded in definition.identityProviders for initial realm creation, but Keycloak’s PUT /admin/realms/{realm} endpoint does not consume the identityProviders or identityProviderMappers fields, so they are silently dropped on realm updates. To declaratively manage identity providers and their mappers on existing realms (including the master realm), use the dedicated CRDs:
- KeycloakIdentityProvider — manages the identity provider instance.
- KeycloakIdentityProviderMapper — manages claim, role, and attribute mappers attached to an identity provider.
Preserving Realm on Deletion
To keep the realm in Keycloak when deleting the CR:
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakRealm
metadata:
name: my-realm
annotations:
keycloak.hostzero.com/preserve-resource: "true"
spec:
instanceRef:
name: my-keycloak
realmName: my-realm
definition:
enabled: true
See Common Patterns for more details.
Short Names
| Alias | Full Name |
|---|---|
kcrm | keycloakrealms |
kubectl get kcrm