The module lifecycle stageExperimental

The module has requirements for installation

v0.0.6

Release date: 2026-09-19

A SeaweedFS store now sizes its volume budget and its integrity checks from the capacity it was asked for, instead of from constants that capped every volume server at 256 GiB and every scrub at twelve hours.

Highlights

Changes in this release:

  • A store no longer stops taking writes at 256 GiB per volume server: the volume size and the slot count follow spec.storage.sizePerNode, so a full store is a full disk.
  • The capacity a store publishes is bounded by what its volume slots can address, so the fill alerts describe the state the store is actually in.
  • An integrity check budgets its time by how much there is to read, and a pass that read nothing no longer counts as a check.

New features

This release adds:

  • SeaweedFSStore.spec.volumeIndex chooses where a volume server keeps its needle map: Memory (the default, unchanged behaviour) or LevelDB. With LevelDB the index cost is bounded by the volume count rather than by the number of objects, and the Pod reserves it.
  • The alert D8SdsObjectStoreScrubIncomplete reports an integrity check that left volume servers unchecked — a store can be scrubbed on schedule and still have part of it never read.

Improvements

This release improves:

  • SeaweedFSStore.spec.volumeServers must now be at least the copy count in spec.replication, and a store that asks for fewer is rejected at creation instead of never provisioning a bucket.
  • The alert D8SdsObjectStoreAlmostFull fires above 90% instead of 95% — the point where the SeaweedFS master stops placing new volumes on a disk, so the warning no longer arrives after the store has already stopped taking buckets.
  • A store that has run out of places for a volume says so in terms of what breaks: writes into existing buckets fail too, not only bucket creation.
  • The API types and the CRDs now carry the same validation, including nine rules that previously existed only in the manifests; USAGE documents which fields a live store will and will not let you change.

Fixes

This release fixes:

  • A SeaweedFSStore whose volume servers held more than 256 GiB each stopped accepting writes with No writable volumes and no free volumes left while its disks were nearly empty, and the store’s own status reported plenty of room.
  • An integrity check over a store larger than roughly three volume servers of 2Ti was cut off mid-pass, and the volume servers it never reached were reported as unreachable rather than as unchecked.
  • spec.storage.sizePerNode was accepted and silently ignored on an existing store: it is the size of a StatefulSet volume claim template, so a new value reached no disk. It is now immutable, with the reason in the rejection message.
  • The controller’s metrics endpoint could not authenticate scrapes, so the module’s own metrics were missing.

Security updates

Security updates in this release:

  • The dependencies of the controller and SeaweedFS images were upgraded to the versions that close the findings the vulnerability scan reports.

Breaking changes

Changes that affect backward compatibility:

  • spec.storage.sizePerNode and spec.masters are immutable after creation, and spec.volumeServers may only be increased. Edits that used to be accepted — and, in the case of sizePerNode, quietly did nothing — are now rejected.
  • A SeaweedFSStore with fewer volume servers than spec.replication asks copies for is rejected. Such a store could never place a volume, so nothing that worked before stops working.

Upgrade notes

Before upgrading, note the following:

  • Nothing has to be done by hand. Existing stores keep their disks and their data: the masters and volume servers restart to pick up the new budget, which only ever raises a store’s ceiling, and the volumes already written are directed to again.
  • A store sitting between 90% and 95% full will raise D8SdsObjectStoreAlmostFull after this upgrade. That is the point at which the master has stopped placing new volumes on its disks, which is what the alert now reports.
  • A store whose spec.volumeServers is below the copy count in spec.replication keeps running and keeps its status updated, but any other edit to it is refused until the count is raised.

Docs

Documentation changes:

  • The Russian translation of the CRD reference and of DESIGN was brought back in step with the English text.

v0.0.5

  • Fix: spec.placement (nodeSelector and tolerations) is now finally applied. The field has been in the API from the start and documented as affecting data plane pods, but it was never read — the API accepted it, but pods were scheduled as if nothing was specified. It now applies to all pods deployed by the module: masters, volume servers, and filers.
  • Replicas of the same component are spread across different nodes. For masters and volume servers, the rule is strict: three volume servers on the same node cannot survive the failure of that node, and the replication code promised copies on other servers. A replica that does not get its own node remains in Pending — this is the visible form of an impossible promise. Filers prefer to spread out, but will still start even when they cannot: they sit in front of metadata, which resides elsewhere.
  • The update will affect already deployed storage: masters and volume servers will be restarted, and on a cluster where there are fewer nodes than replicas, some pods will not come up until the number of replicas is reduced or nodes are added.
  • A new field spec.postgresClassName has been added — the name of the PostgresClass from which the managed metadata database is deployed. This is the only way to influence the placement of its pods: the Postgres resource has no scheduling fields at all; tolerations and nodeSelector belong to the PostgresClass, so spec.placement does not reach the database. If empty, the default class is used.

v0.0.4

  • Storage reports its capacity: status.capacity with total, used, free, and used fraction — on both backends. Until at least one node responds, the field is not populated: “unknown” and “empty” are different things
  • Module metrics, alerting rules, and dashboard: storage readiness and its fill level. Alerts warn about fill above 85% and above 95%, and storage that is not Ready for half an hour is reported separately from storage that has simply stopped reporting
  • Filer metadata can be kept in an external PostgreSQL: metadataStore External and a Secret with connection parameters (host, port, database, username, password, sslmode, ca.crt), including server verification with a custom CA. The connection is verified before filers start, and sslmode disable and values unknown to libpq are rejected — passwords and all object names travel over this connection
  • Bucket lifecycle expiration rules: spec.lifecycle with expireAfterDays, expireNoncurrentAfterDays, and abortIncompleteUploadsAfterDays and an optional prefix; each rule requires a unique id. Rules are removed from a bucket when they are deleted from the Bucket. There is no storage tier transition: one of the engines does not perform it, and a field that silently does nothing is not introduced
  • A bucket that already exists in the backend and is not owned by the module is no longer picked up by name: the module marks its own buckets and refuses to touch another’s, reporting this in the BucketNotOwnedByModule condition
  • The documentation now includes a matrix of S3 operation support across backends, indicating the engine versions for which it is filled
  • Fix: filers restart when the metadata database connection changes — previously, password rotation updated the configuration, but running filers continued to use the old credentials
  • Fix: the Secret with filer configuration is no longer overwritten on every reconciliation cycle
  • Internal changes: unit tests for reconcilers, e2e scenarios, and internal project documentation

v0.0.3

  • The resource model has been redesigned and is incompatible with the previous version — the module no longer manages resources created under the old model; they must be recreated
  • The System and Lightweight profiles have been removed along with the Garage backend. Two backends remain — SeaweedFS and Ceph RGW
  • ObjectStore no longer deploys storage; instead, it describes a class modeled after StorageClass: it references a store via spec.storeRef, while the storage itself is described by the new SeaweedFSStore and SDSElasticStore resources
  • BucketClaim is now called Bucket and remains in the namespace, while the previous cluster-wide Bucket is now called BucketContents. Data saved with reclaimPolicy Retain waits in the Released phase and is picked up by a newly created Bucket
  • BucketClaimPolicy has been removed. Along with it, shared access to a single bucket from multiple namespaces and binding to an existing bucket via regex are gone
  • Publishing the S3 endpoint externally: spec.publish on the store defines the hostname, Gateway, and TLS certificate; the module creates a listener and route and publishes the external address in status.endpoint.external. Publishing without TLS is prohibited
  • The spec.endpointScope field in BucketAccess selects which address — internal or external — is placed in the Secret with credentials; when the endpoint changes, issued Secrets are reissued
  • Setting PublicRead in the bucket’s spec.accessPolicy enables anonymous object reading on both backends. Listing bucket contents is not provided in this case
  • Versioning and Object Lock on both backends: GOVERNANCE and COMPLIANCE modes, retention period, and legal hold. Deleting data under an unexpired retention is blocked, and the reason for failure is recorded in the status
  • Data integrity report: status.integrity and condition IntegrityHealthy. On SeaweedFS, the module automatically runs scrubbing on a schedule (spec.integrity) and, with separate permission, replaces a damaged copy; on Ceph RGW, the scrub state is taken from the cluster
  • Data copy accounting: status.redundancy and condition RedundancyHealthy report volumes that have fewer copies than requested by replication
  • Metrics, alerts, and a Grafana dashboard for data integrity, copy shortage, and overdue scrubbing
  • Stored data encryption: spec.encryption. On SeaweedFS — server-side encryption with a key from a Secret; key rotation is not applied silently but held for confirmation, otherwise already written objects would become unreadable. On Ceph RGW, keys are taken from Deckhouse Stronghold
  • Data plane components are secured with network policies
  • Fix: the RGW user identifier fits within Rook’s label length limit — previously, a bucket on Ceph RGW could not become Ready
  • Fix: the bucket policy is assembled from the entire cluster state, not via incremental patches — eliminating races when multiple accesses target the same bucket
  • Fix: the ‘data is held by retention’ failure is determined from the bucket itself, not its spec — previously, any access error looked the same and permanently held the finalizer
  • Fix: deleting a Bucket wakes up its content controller, and a deleting Bucket is no longer considered a live owner — otherwise, a bucket could remain in the backend without a single reference to it
  • Fix: the number of slots for SeaweedFS volumes is no longer calculated based on disk size; a volume grows one per bucket
  • Fix: a store does not report readiness if it cannot accept a bucket
  • Fix: scrubbing is performed outside the reconciliation loop — previously, it did not fit within its budget and did not complete; a pass that covers no volumes is not considered a check
  • Unified logger and metrics server for the controller
  • Internal changes to the module build

v0.0.2

  • Bugfix: the data-plane images (Garage, SeaweedFS) are pulled with the module-scoped registry secret - previously the pull failed in clusters with a private registry
  • CVE fixes
  • lib-helm updated to 1.72.13

v0.0.1

  • Initial release of the module in the Experimental stage. The module manages S3-compatible object storage through a set of ObjectStore, Bucket, BucketClaim, BucketAccess and BucketClaimPolicy resources
  • ObjectStore deploys and maintains the storage itself according to one of four profiles: System (Garage on control-plane nodes, local PVs), Lightweight (Garage on PVC), Full (SeaweedFS) and Heavy (Ceph RGW on an existing sds-elastic cluster)
  • BucketClaim requests a bucket — either its own private one or a binding to a shared bucket allowed by a BucketClaimPolicy resource in the bucket owner’s namespace (deny by default)
  • BucketAccess issues a separate access key and writes a Secret with standard connection variables; the key is rotated using the storage.deckhouse.io/rotate annotation
  • Module resources publish status.conditions and status.observedGeneration