The module lifecycle stage: Experimental
The module has requirements for installation
Before enabling the module, you need to allow the use of experimental modules in the deckhouse ModuleConfig. To do this, set the allowExperimentalModules parameter to true.
v0.1.0
Release date: 2026-10-01
ElasticCluster and ElasticStorageClass are namespaced, so one Kubernetes cluster can run several Ceph clusters, one per namespace (a breaking change of both resources, existing ones are migrated automatically); OSDs can be encrypted with keys kept in Deckhouse Stronghold, and the module ships Ceph dashboards and alerts.
Highlights
Changes in this release:
- Several Ceph clusters in one Kubernetes cluster:
ElasticClusterandElasticStorageClassare namespaced, a namespace holds oneElasticCluster, and Rook deploys that cluster into the namespace of theElasticClusteritself. Existing resources are moved intod8-sds-elasticon upgrade, and the running Ceph cluster is adopted unchanged. - OSD encryption:
spec.storage.encryptedwraps every OSD in dm-crypt (LUKS2),spec.storage.encryptionKeyStorekeeps the keys in Deckhouse Stronghold instead of Secrets of the cluster, andspec.storage.keyRotationreplaces them on a schedule. - Monitoring out of the box: three Grafana dashboards (cluster, pools, single OSD) and twelve alerts on the health, the data and the capacity of every Ceph cluster the module runs, in any namespace.
New features
This release adds:
ElasticClusterandElasticStorageClassare namespaced. A namespace holds at most oneElasticCluster; a second one is rejected at admission with a message naming the first, so several Ceph clusters mean several namespaces. AnElasticStorageClassresolvesspec.clusterRefin its own namespace only.- Rook deploys a Ceph cluster into the namespace of its
ElasticCluster. The controller mirrors into that namespace the ServiceAccounts, Roles, RoleBindings and the registry pull Secret the Ceph daemons need (Rook ClusterRoles are copied into Roles of the same name, which a Project’s namespace accepts), and removes them when the cluster is deleted; the namespace itself is never created or deleted by the module. The namespace has to carrysecurity.deckhouse.io/pod-policy: privileged, which exempts everything else in it as well, so a Ceph cluster is best given a namespace of its own. The namespace of a Project is not relabelled: the cluster waits atNamespaceReady=Falseuntil the Project grants the profile (podSecurityProfile: Privileged, or the label inspec.parameters.namespace.labels). A namespace outside any Project is labelled by the controller, which records that in thesds-elastic.deckhouse.io/added-labelsannotation and removes the label on teardown. A namespace deliberately labelledbaselineorrestrictedis refused, not overridden. Direct writes to the Rook resources of a cluster (internal.sdselastic.deckhouse.io) are refused in every namespace, as they were ind8-sds-elastic. ElasticClusterpublishes aNamespaceReadycondition, ahead ofStorageReady, that reports whether the namespace can hold the Ceph cluster and names what is missing.spec.storage.encrypted: trueon anElasticClusterwraps every OSD in dm-crypt (LUKS2), so the data and Ceph metadata on a disk that leaves the cluster stay unreadable. It does not protect a running cluster or the network traffic. The field is immutable: it can only be set when the cluster is created. Measured cost: about 9% of the throughput on small random writes and up to 26% on large sequential reads, and OSDs take about twice as long to start. The nodes need thedm_cryptkernel module.spec.storage.encryptionKeyStorechooses where the OSD key encryption keys live:KubernetesSecret(default, Secrets in the cluster’s namespace) orStronghold(Deckhouse Stronghold or any store serving the Vault API over KV v2), configured bystronghold.address,kvPath,role,authMountPath(defaultkubernetes_local),caCertSecretNameorinsecureSkipTLSVerify. It requiresencrypted: trueand is immutable. The module does not prepare Stronghold; the KV mount, the policy and the role are created by its administrator. The key is read on every OSD start, so an unreachable or sealed store keeps restarted OSDs from coming up, while running ones keep serving.spec.storage.keyRotation(enabled, optional cronschedule, Rook’s weekly default when unset) has Rook replace the key encryption key of every OSD on a schedule, for both key stores. Data is not re-encrypted and OSDs keep serving. It requiresencrypted: trueand, unlike the two fields above, can be turned on, rescheduled or turned off on a running cluster.- Three Grafana dashboards in the Storage folder: SDS-ELASTIC / CEPH CLUSTER, SDS-ELASTIC / CEPH POOLS and SDS-ELASTIC / CEPH OSD (per OSD, to find the slow disk). Per-daemon counters are scraped from ceph-exporter by a
ServiceMonitorthe module ships; cluster-wide metrics come from the mgr through themonitoring-custommodule. - Twelve alerts on every Ceph cluster of the module, each carrying the
namespaceof itsElasticCluster:D8SdsElasticCephClusterHealthError,D8SdsElasticCephClusterHealthWarning,D8SdsElasticCephMonOutOfQuorum,D8SdsElasticCephOSDDown,D8SdsElasticCephPlacementGroupsUnavailable,D8SdsElasticCephPlacementGroupsDegraded,D8SdsElasticCephPlacementGroupsInconsistent,D8SdsElasticCephObjectsUnfound, and the capacity alertsD8SdsElasticCephClusterFillingUp(75%),D8SdsElasticCephClusterAlmostFull(85%),D8SdsElasticCephOSDNearFull(one OSD over 85%) andD8SdsElasticCephPoolAlmostFull(one pool over 85%). They select the module’s own scrape jobs, so a Ceph cluster run by something else does not raise them, and none fires where the module is only installed.
Improvements
This release improves:
CephClusterReadyof anElasticClusterreports the reasonStalled, with the time spent, once the storage backend has not become ready for 15 minutes, so a cluster that will never assemble is told apart from a slow one. The condition staysFalsein both cases.- A teardown that outlives its expected duration reports
TeardownStalledwith the elapsed time on theReadycondition: after 2 minutes for both anElasticStorageClassand anElasticCluster. Reasons that already say what to do (BoundVolumesExist,DataPresentInPool,FilesystemNotEmpty,StorageClassesExist,VolumesExist) are kept. AnElasticStorageClasswith force deletion whose pool still refuses to go says so. - While csi-ceph keeps the
CephClusterConnectionof a cluster being deleted for volumes or snapshots, theElasticClusterteardown reportsVolumesExist, names up to ten remaining PersistentVolumes and tells to delete them, or to delete the remaining VolumeSnapshots, instead ofTerminatingand laterTeardownStalled. It needs csi-ceph v0.5.37 or later, which reports thePersistentVolumesExistandVolumeSnapshotContentsExistreasons; with an older one nothing changes. - The host directory of a cluster’s mon store is pinned in the new
ElasticCluster.status.dataDirHostPathfield. A new cluster gets/var/lib/deckhouse/sds/elastic/<namespace>/<name>; an existing cluster keeps the path it runs on (/opt/deckhouse/sds/elastic/<name>), with no migration. A change of the recorded value is rejected; clearing it makes the controller record it again from the running cluster. - csi-ceph provisions and mounts volumes as a Ceph user of its own,
client.sds-elastic-csi, which the controller creates in every cluster with anaescephx key instead of usingclient.admin. Ceph 19.2.6 creates new keys asaes256k, which the kernel client (krbd and the CephFS kernel mount) understands only from Linux 7.0; with this user, kernel mounts keep working on older kernels. - The
AUTH_INSECURE_*health checks that Ceph 19.2.6 raises foraeskeys do not take a cluster out ofCephClusterReady, do not block the next Ceph upgrade and do not fire the health alerts. None of them affects a volume. - On Deckhouse 1.78 and later the module’s access roles follow the capability scheme:
d8:system-capability:sds-elastic:viewandd8:system-capability:sds-elastic:editgrantElasticClusterandElasticStorageClassin all namespaces andElasticClusterCredential. Older Deckhouse versions keep thed8:manage:permission:module:sds-elastic:*roles.
Fixes
This release fixes:
- An
ElasticClusterno longer reportsCephClusterReadyandReadywhile its storage serves nothing. Readiness followed only the phase of the RookCephCluster, which staysReadyeven when every OSD is crash-looping; now anyHEALTH_ERRcheck, or inactive placement groups (PG_AVAILABILITY), hold the condition back and are named in its message. - An
ElasticClusterre-created under the name of a deleted one serves volumes again. TheElasticClusterCredentialleft by the previous cluster kept its oldfsidand admin key, which made every PVC fail withPermission deniedwhile the cluster reportedReady; such a credential is now dropped and rebuilt for the new cluster. The credential of a deleted cluster is still kept as long as no cluster of that name comes back. - Controller metrics are collected again. The kube-rbac-proxy in front of them could not authenticate scrapes, so the target stayed at
up == 0with a permanentTargetDown. - The Warning event with which Rook reports a failed reconcile of a
CephClusteris no longer lost on a large cluster: a message over the 1024-character API limit was rejected and the event was never recorded. It is now shortened for the event, and the full text stays in the Rook operator log. - The
ElasticClusterteardown deletes the Ceph user of csi-ceph and the RookCephClusteronly after theCephClusterConnectionis gone. Deleted in the same pass, they left the PersistentVolumes that csi-ceph still had to detach or delete with no way to do it, and theElasticClusterstayed in deletion forever. - Deleting an
ElasticStorageClassno longer removes its pool while a volume still needs it. The guard counted onlyBoundPersistentVolumes, so right after the last PVC was deleted the pool went while the volume was still attached to a node or waiting for itsDeletereclaim: the detach then failed for good withpool ... not found, theVolumeAttachmentstayed, the PersistentVolume could not be deleted, and theElasticClusterdeletion that followed never finished. The pool is now kept, with the reasonBoundVolumesExist, while a PersistentVolume of the StorageClass isBound, attached to a node, orReleased/Failedwith theDeletereclaim policy; aRetainvolume attached nowhere does not hold it.
Security updates
Security updates in this release:
golang.org/x/cryptois upgraded to v0.56.0 in the Rook operator and to v0.57.0 in the module hooks, closing CVE-2026-56854, CVE-2026-56855 and CVE-2026-78662 reported by image scanners against v0.53.0.
Breaking changes
Changes that affect backward compatibility:
ElasticClusterandElasticStorageClassare namespaced resources. A manifest kept for re-applying needsmetadata.namespace(d8-sds-elasticfor resources that exist before the upgrade), andkubectl getandeditneed-n <namespace>or-A. Only oneElasticClusteris accepted per namespace.- The mgr metrics of the module’s Ceph clusters are scraped under
job="custom-sds-elastic-ceph"instead ofjob="custom-ceph": the mgr Pods carryprometheus.deckhouse.io/custom-target: sds-elastic-cephinstead ofceph.
Upgrade notes
Before upgrading, note the following:
- The upgrade migrates existing
ElasticClusterandElasticStorageClassresources into thed8-sds-elasticnamespace by itself: the objects are saved into thesds-elastic-crd-scope-migration-backupSecret of that namespace, the CustomResourceDefinitions are re-created namespaced (their scope cannot be changed in place), and the objects are restored with their status. The running Ceph cluster is adopted, not rebuilt: ind8-sds-elasticevery object name, the host path and the connection to csi-ceph stay as they were. Saving the resources beforehand (d8 k get elasticclusters,elasticstorageclasses -o yaml) is still a good idea. - For the duration of the migration the controller is stopped and the
ElasticClusterandElasticStorageClassadmission webhooks are removed; the Helm release that follows in the same converge restores both. - Add
metadata.namespace: d8-sds-elasticto the manifests of existingElasticClusterandElasticStorageClassresources kept in Git or elsewhere. Applied without it, a manifest creates a new resource in the current namespace instead of updating the migrated one. - An
ElasticStorageClassname has to be unique across the whole cluster, not just within its namespace, because it becomes the name of a cluster-wideStorageClass; a name already taken in another namespace is rejected. - Queries and dashboards of your own that read the Ceph mgr metrics by
job="custom-ceph"have to switch tojob="custom-sds-elastic-ceph". - Existing clusters are rolled from Ceph 19.2.5 to 19.2.6 by the controller’s upgrade stage, with no action needed. csi-ceph switches from
client.admintoclient.sds-elastic-csi, which the controller creates in existing clusters too; volumes that are already mounted are not affected. - From Ceph 19.2.6 on,
ceph health detailshowsAUTH_INSECURE_KEYS_ALLOWED,AUTH_INSECURE_KEYS_CREATABLEandAUTH_INSECURE_CLIENT_KEY_TYPEwarnings on every cluster, and a cluster created before 19.2.6 also showsAUTH_INSECURE_SERVICE_KEY_TYPEandAUTH_INSECURE_SERVICE_TICKETSerrors for its daemons’aeskeys. They are expected; the module’s conditions, upgrade gate and alerts leave them out. - The controller’s RBAC is wider: it reads and labels namespaces, manages ServiceAccounts, Roles, RoleBindings, Secrets and ConfigMaps cluster-wide, may
bindandescalateroles and reads ClusterRoles, because it furnishes the namespace of every Ceph cluster the way the chart furnishesd8-sds-elastic.
Known issues
Known limitations of this release:
- The Pods of a Ceph cluster outside
d8-sds-elasticare not covered by the Deckhouse ban onkubectl execintod8-*namespaces: anyone allowed to exec in that namespace can exec into its Ceph daemons.
Docs
Documentation changes:
- New section on namespaces and the number of Ceph clusters: one
ElasticClusterper namespace, what is mirrored into it, the privileged Pod Security profile and how to grant it through a Project, and what happens to clusters created before the change. - New sections on OSD encryption: what it covers, the measured cost per load profile, the Stronghold preparation (KV mount, a policy that also grants
sys/mounts, which Rook reads before writing the first key, and the role), what happens when the key store is unavailable or sealed, and key rotation. - The deletion procedure for an
ElasticClusternow closes the dm-crypt mappings of an encrypted cluster before its LVM volumes are removed, and says that re-creating a cluster under the same name requires removing itsdataDirHostPathon every storage node, while the OSD disks need no wiping. The table of teardown reasons describesVolumesExistandTeardownStalled. spec.storage.nodeSelectorandspec.storage.blockDeviceSelectordocument that an empty selector{}selects every Node, control-plane Nodes included, and every consumable BlockDevice on them, and that disks appearing later join the cluster on their own.- New section on monitoring and alerts: where the metrics come from, the dashboards, and when each alert fires.
- The Russian usage guide is rewritten in plain language, and both locales use the Ceph CLI of the Rook operator Pod instead of a Rook toolbox, which the module does not deploy.
Dependencies
Dependency updates:
Rook:1.19.5→1.20.7(changelog)Ceph (squid):19.2.5→19.2.6(changelog)- The version the controller deploys. Existing clusters are upgraded automatically.
Ceph (tentacle):20.2.3→20.2.4(changelog)- The image is built and shipped; the controller does not deploy it.
v0.0.5
Release date: 2026-09-02
ElasticCluster networks are described as lists of CIDRs (a breaking change of the resource schema, migrated automatically), an ElasticStorageClass can reserve a share of the filesystem for the superuser, and release notes carry sections instead of one flat list.
Highlights
Changes in this release:
ElasticCluster.spec.network.publicand.clusteraccept several CIDRs, so a Ceph cluster whose storage nodes sit in more than one host subnet can be described. Both fields are lists now, which is a breaking change of the resource schema; existing resources are migrated on upgrade.- An
ElasticStorageClasscan ask for a share of the filesystem to be kept for the superuser — the classic ext4 reserve — with thestorage.deckhouse.io/ext4-reserved-percentannotation. Volumes are formatted with no reserve unless it is asked for. - A release now has a summary and per-kind sections — new features, improvements, fixes, security, breaking changes, upgrade notes — on the module’s release-notes page and in
ModuleRelease.spec.changelog, where the Deckhouse console renders it.
New features
This release adds:
ElasticCluster.spec.network.publicand.clustertake one or more CIDRs each, which is what a cluster whose storage nodes span several host subnets needs: every Ceph daemon binds to a host interface and has to own an address inside one of the given ranges.storage.deckhouse.io/ext4-reserved-percenton anElasticStorageClasssets the percentage of a volume kept for the superuser: a whole number from 0 to 50, default 0. The module copies the resolved value onto theCephStorageClassit generates, so clearing the annotation returns the class to the default. It applies to volumes created afterwards — an existing filesystem keeps the reserve it was created with — and only totype: RBD, because a CephFS volume is a subdirectory of an existing filesystem and is never formatted. An invalid value leaves theElasticStorageClasswith its storage-class stage failing and the reason in its status, instead of breaking volume creation later.
Improvements
This release improves:
- An
ElasticClusterwhosespec.storage.nodeSelectorselects nodes that the module settingsettings.dataNodes.nodeSelectorexcludes now says so: theStorageReadycondition names the nodes that carry no sds-node-configurator agent and the setting to widen, instead of reporting that no BlockDevice matches the selectors. A selector that matches no node at all is reported separately from an empty BlockDevice set. - Release notes are written per locale in
.release-notes/<tag>.yamland<tag>.ru.yamlwith the sectionssummary,highlights,new_features,improvements,fixes,security,breaking,upgrade_notes,known_issues,docsanddependencies. Both locales reach the cluster, so the console shows the notes in the language of the reader. - The release history before this version keeps the previous flat format and renders on the same page exactly as before; nothing had to be rewritten.
Fixes
This release fixes:
- A release tag with no release notes used to publish an empty
changelog.yaml, leavingModuleRelease.spec.changelogblank with a green build. Such a tag now fails the build.
Breaking changes
Changes that affect backward compatibility:
ElasticCluster.spec.network.publicand.clusterare lists of CIDRs, not single strings. A manifest kept for re-applying has to be updated to the list form; resources that already exist are migrated on upgrade.
Upgrade notes
Before upgrading, note the following:
- An ElasticCluster created before this version is migrated automatically on upgrade: each of
spec.network.publicand.clusterbecomes a one-element list holding the same address. No daemon is restarted and nothing has to be done by hand.
Docs
Documentation changes:
- The section on reserving part of a volume for the superuser is new, with the annotation, its range and what it does not cover.
- The documentation of
settings.dataNodes.nodeSelectornow states what the data-node label actually controls — whether the sds-node-configurator agent runs on a node, and therefore whether that node has any BlockDevice — how it relates toElasticCluster.spec.storage.nodeSelector, and that the empty default covers control-plane nodes too.
v0.0.4
- Fix: caBundle for webhooks is now sourced from the webhooks-https-certs secret, and the module’s service accounts are excluded from validation — a broken webhook no longer leaves ElasticCluster without a status and Ceph undeployed
- Fix: a fresh cluster no longer enters HEALTH_WARN RECENT_MGR_MODULE_CRASH — mgr modules that cannot load (dashboard, restful, cephadm, diskprediction_local, rook) have been removed from the image
- Controller metrics are now published via kube-rbac-proxy and collected by a ServiceMonitor
- The module has been migrated to the common logger; the logging level is taken from the module settings, as documented
- Fix: rebuilding the same tag no longer leaves an outdated changelog in the release image
- Base images updated to v2.1.7, Go to 1.27.0, and lib-helm to 1.72.18
- Internal changes to the module build and e2e tests
v0.0.3
- Build changes
v0.0.2
- Added the ability to control Rook object-store CRs from the sds-object controller (webhooks)
- ElasticClusterCredential publishes status.conditions with the Ready condition; the status is generated through the common conditions library, status.conditions in the CRD is declared as a keyed list for the correct server-side apply
- Fix: VPA gate for the presence of a CRD, and not for the list of enabled modules
- Fix: The Ceph cluster no longer goes into HEALTH_WARN - the non-working mgr module restful has been disabled, a dependency has been added to load the telemetry mgr module
- Build Ceph images moved to container-base + pm; supported versions - 19.2.5 (squid) and 20.2.3 (tentacle)
- Documentation clarified: spec.network.public/cluster should be host subnets
- Update base images to v2.1.2, Go to 1.26.6 and lib-helm to 1.72.14
- Internal changes in module assembly
v0.0.1
- Initial release of the module
- Added module setting
settings.dataNodes.nodeSelector. The controller places the labelstorage.deckhouse.io/sds-elastic-node=""on each Kubernetes node that matches the selector and removes the label from nodes that no longer match it. Downstream components (sds-node-configurator agent, ElasticCluster hosting) can use this label as nodeAffinity (pod-node affinity rule) to select data nodes.