This release includes 4 breaking changes for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Topics
Affected surfaces
Summary
AI summaryBroad release touches Upgrade Guide, Breaking changes, deps, and Highlights.
Full changelog
This release unifies resource identity across all CRDs: every resource now declares its Keycloak identifier in a dedicated, immutable spec field instead of inside the raw definition blob. It also scopes namespaced references to their own namespace, adds declarative role and group assignment on KeycloakUser (including client service accounts), and fixes duplicate user-profile components and an organization reconcile loop.
Breaking changes
Typed identifier fields on all resource CRDs (#101)
Every resource CRD now identifies its Keycloak object through a dedicated spec field. The field is required, is validated at admission, and is immutable once set (changing an identifier previously orphaned the old Keycloak object silently).
| CRD | Identifier field |
|-----|------------------|
| KeycloakRealm, ClusterKeycloakRealm | spec.realmName |
| KeycloakClient | spec.clientId |
| KeycloakUser | spec.username (omit for service accounts via clientRef) |
| KeycloakGroup, KeycloakRole, KeycloakClientScope, KeycloakComponent, KeycloakOrganization, KeycloakProtocolMapper, KeycloakIdentityProviderMapper | spec.name |
| KeycloakIdentityProvider, KeycloakRequiredAction | spec.alias |
Migration
Add the spec field carrying the same value the definition uses. An identifier inside spec.definition is tolerated only when it matches the spec field, so existing definitions do not need to be rewritten; a conflicting value is rejected.
# Before
spec:
definition:
realm: production
enabled: true
# After
spec:
realmName: production # new: required, immutable
definition:
realm: production # optional; must match spec.realmName if present
enabled: true
Unmigrated resources report InvalidIdentifier in their status and pause reconciliation until the spec field is added — nothing is deleted. The resolved identifier is surfaced in each resource's status (e.g. status.realmName) and is what dependent resources consume.
Also note: the KeycloakClient status field status.clientID was renamed to status.clientId.
Namespaced references no longer cross namespaces (#90)
The namespace property is removed from all namespaced refs (realmRef, instanceRef, clientRef, clientScopeRef, userRef, roleRef, groupRef, parentGroupRef, identityProviderRef); they now always resolve in the referring resource's own namespace. The field is silently pruned by the API server, not rejected — audit your manifests before upgrading.
Cross-namespace sharing remains fully supported through the cluster-scoped kinds: point resources in any namespace at a shared ClusterKeycloakRealm (clusterRealmRef) or ClusterKeycloakInstance (clusterInstanceRef), or co-locate the resources in one namespace.
Role and group assignments move out of the KeycloakUser definition (#94)
realmRoles, clientRoles, and groups keys inside spec.definition are now rejected with InvalidDefinition. They were previously ignored (Keycloak's user endpoint does not apply them), which commonly bit users pasting exported user representations. They are now typed spec fields that are actually reconciled — see Highlights below.
# Before (keys were silently ignored)
spec:
username: jdoe
definition:
enabled: true
realmRoles: ["offline_access"]
groups: ["developers"]
# After (authoritatively reconciled)
spec:
username: jdoe
definition:
enabled: true
realmRoles:
- offline_access
groups:
- developers
Stricter admission validation for references (#105)
CRD schemas now enforce what the API documentation always required: exactly one of realmRef/clusterRealmRef (or clientRef on users), at most one of role/roleRef, exactly one role-mapping subject, and so on. Manifests that were previously accepted with ambiguous or missing references are now rejected at admission time.
Highlights
- Identifier unification — uniform identity handling across all resource CRDs: typed, immutable identifier fields with CEL validation, resolved identifiers surfaced in status for dependent controllers, and deletion paths that no longer issue Keycloak calls with empty identifiers. See the new Spec Layout section for the architecture behind this. (#101)
- Declarative user roles and groups —
spec.realmRoles,spec.clientRoles, andspec.groupsonKeycloakUserare reconciled through Keycloak's dedicated role-mapping and group-membership endpoints, for regular users and service accounts alike. Each field is authoritative when set (an empty list removes everything) and unmanaged when omitted. Do not combine them withKeycloakRoleMappingresources for the same user. (#94) - Role mappings for service accounts —
KeycloakRoleMappinggainssubject.serviceAccountRefto assign roles to a client's service account without an intermediateKeycloakUser. (#94) - User-profile component adoption —
KeycloakComponentnow adopts the unnameddeclarative-user-profilecomponent Keycloak creates when the user profile is saved through the Admin UI or/users/profileAPI, instead of creating a duplicate. (#104) - Organization drift detection —
KeycloakOrganizationcompares desired and current state before updating, ending the continuous PUT loop that flooded logs and Keycloak. (#96) - Nightly container builds (#102) and GitHub Actions pinned to immutable SHAs (#103).
Upgrade Guide
1. Upgrade the operator first
The new identifier fields do not exist in the v0.9.0 CRD schemas and would be pruned if you applied migrated manifests before upgrading. Upgrade the operator (which updates the CRDs), then migrate manifests. Existing resources keep running; unmigrated ones pause reconciliation with InvalidIdentifier until step 2 — nothing is deleted.
helm upgrade keycloak-operator oci://ghcr.io/hostzero-gmbh/charts/keycloak-operator \
--namespace keycloak-operator \
--version 0.10.0
2. Migrate manifests
For every resource:
- Add the typed identifier field (see the table above) with the value currently used inside
spec.definition. - Remove
namespacefrom any refs; switch cross-namespace setups toclusterRealmRef/clusterInstanceRef. - On
KeycloakUser, moverealmRoles/clientRoles/groupsout ofspec.definitioninto the spec fields. - Ensure exactly one realm/instance/subject reference is set per resource.
What's Changed
- Feat/same namespace refs by @fkold in https://github.com/Hostzero-GmbH/keycloak-operator/pull/90
- fix: prevent continious update organization loop by @nejec in https://github.com/Hostzero-GmbH/keycloak-operator/pull/96
- chore: add nightly container builds by @nejec in https://github.com/Hostzero-GmbH/keycloak-operator/pull/102
- ci: pin GitHub Actions to immutable SHAs by @luxass in https://github.com/Hostzero-GmbH/keycloak-operator/pull/103
- fix(crd): enforce mutually exclusive references by @luxass in https://github.com/Hostzero-GmbH/keycloak-operator/pull/105
- chore(deps): bump softprops/action-gh-release from 3.0.0 to 3.0.1 by @dependabot[bot] in https://github.com/Hostzero-GmbH/keycloak-operator/pull/108
- chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 by @dependabot[bot] in https://github.com/Hostzero-GmbH/keycloak-operator/pull/107
- chore(deps): bump the kubernetes group with 3 updates by @dependabot[bot] in https://github.com/Hostzero-GmbH/keycloak-operator/pull/114
- chore(deps): bump the github-actions group with 9 updates by @dependabot[bot] in https://github.com/Hostzero-GmbH/keycloak-operator/pull/115
- chore(deps): bump the github-actions group with 2 updates by @dependabot[bot] in https://github.com/Hostzero-GmbH/keycloak-operator/pull/119
- chore(deps): bump github.com/prometheus/client_golang from 1.23.2 to 1.24.1 by @dependabot[bot] in https://github.com/Hostzero-GmbH/keycloak-operator/pull/118
- chore(deps): bump github.com/go-logr/logr from 1.4.3 to 1.4.4 by @dependabot[bot] in https://github.com/Hostzero-GmbH/keycloak-operator/pull/117
- chore(deps): bump the kubernetes group with 3 updates by @dependabot[bot] in https://github.com/Hostzero-GmbH/keycloak-operator/pull/116
- Feat: identifier unification by @fkold in https://github.com/Hostzero-GmbH/keycloak-operator/pull/101
- feat: reconcile realm roles, client roles, and groups on KeycloakUser by @mohamedhabas11 in https://github.com/Hostzero-GmbH/keycloak-operator/pull/94
- fix: adopt existing user profile components by @luxass in https://github.com/Hostzero-GmbH/keycloak-operator/pull/104
New Contributors
- @fkold made their first contribution in https://github.com/Hostzero-GmbH/keycloak-operator/pull/90
- @nejec made their first contribution in https://github.com/Hostzero-GmbH/keycloak-operator/pull/96
- @mohamedhabas11 made their first contribution in https://github.com/Hostzero-GmbH/keycloak-operator/pull/94
Full Changelog: https://github.com/Hostzero-GmbH/keycloak-operator/compare/v0.9.0...v0.10.0
Breaking Changes
- Typed immutable identifier fields added to all resource CRDs (e.g., spec.realmName, spec.clientId) and required for reconciliation; definition‑based identifiers are deprecated.
- Namespaced reference `namespace` field removed; refs now resolve only within the referring resource's own namespace. Cross‑namespace use must employ cluster‑scoped kinds like ClusterKeycloakRealm.
- `KeycloakUser` role and group keys (`realmRoles`, `clientRoles`, `groups`) inside `spec.definition` are rejected; they must be moved to typed spec fields for reconciliation.
- Stricter admission validation enforces mutually exclusive reference fields (e.g., exactly one of realmRef/clusterRealmRef) across all CRDs.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About Hostzero-GmbH/keycloak-operator
All releases →Beta — feedback welcome: [email protected]