BookKeeperCluster resource and sn-operator expands the existing volumes in place, provided the StorageClass sets allowVolumeExpansion: true.
How it works
Unlike ZooKeeper, BookKeeper does not replicate its full state to every node, so a bookie cannot simply be emptied and restarted. Its ledgers must first be re-replicated elsewhere. Decommissioning does this. When you mark a bookie for decommission and delete its pod, the operator holds the pod via a finalizer and drives auto-recovery to replicate every ledger the bookie holds onto the remaining bookies. Once replication completes, the operator deletes the pod’s volume claims for you and lets the StatefulSet recreate the pod. The new claims are created from the StatefulSet’s volume claim templates, so they pick up whatever storage you configured in Step 1.You do not delete volume claims yourself in this procedure. The operator does it as part of decommissioning, after it has confirmed the data is safe elsewhere. This is the main safety property of the decommission approach.
Prerequisites
kubectlaccess to the namespace running your Pulsar cluster, with permission to annotate pods, delete pods, and edit theBookKeeperClusterresource- Permission to create or modify StorageClasses, depending on which approach you choose below
- Enough spare capacity on the remaining bookies to hold the decommissioned bookie’s data
- No in-flight scaling operation or rolling upgrade on the BookKeeper cluster
Inspect your environment first
Determine the following four things before you begin. 1. Which volumes your bookies use. BookKeeper supports multiple journal and ledger volumes per bookie, so the set of claims depends on your configuration:journal-<index> and ledgers-<index>, one per volume configured in spec.storage.journal.numVolumes and spec.storage.ledger.numVolumes. Apply every step to all volumes the command lists.
2. Which StorageClass they use, and its reclaim policy.
Delete means each old disk is released automatically. Retain means it is not, and you must clean up the old volumes yourself or continue paying for them.
3. That the cluster is healthy, with no under-replicated ledgers.
ensembleSize in use across your namespaces, and confirm the cluster can serve writes with one fewer bookie before starting.
Step 1: Point BookKeeper at the new storage
New claims are created from the StatefulSet’s volume claim templates, so those templates must describe the storage you want before any bookie is decommissioned. Choose the approach that matches your environment.Option A: Change the StorageClass on the BookKeeperCluster
Use this approach whenever you can create a new StorageClass. It confines the change to BookKeeper and leaves every other component untouched.-
Create a new StorageClass for the target disk type, under a new name. The following example provisions AWS EBS gp3 volumes:
-
Set the new StorageClass on the
BookKeeperClusterresource, for both the journal and the ledger volumes: -
sn-operatordetects the change and replaces the StatefulSet so it carries the new templates. The StatefulSet is deleted with an orphan propagation policy, so the running pods and existing volumes survive and are adopted by the replacement. Confirm the new templates are in place before continuing:
sn-operator performs this replacement only while the StatefulSet is Ready and its replica count is not changing. If the templates do not update, see The StorageClass change is not reflected in the StatefulSet.Option B: Recreate the StorageClass in place
Use this approach when you cannot introduce a new StorageClass — for example, when BookKeeper uses the cluster default StorageClass and StorageClass definitions are owned by a platform team or a GitOps pipeline — or when you deliberately want every component to move to the new disk type. A StatefulSet’svolumeClaimTemplates are immutable, so if the operator cannot replace the StatefulSet for you, redefining the StorageClass under the same name is the only way to change what new volumes get.
StorageClass parameters are immutable, so the StorageClass must be deleted and recreated under the same name. Existing volumes are already bound and provisioned, and are unaffected by the deletion. New claims referencing the StorageClass fail until it is recreated, so perform both operations back to back.
-
Back up the current definition:
-
Copy the backup and modify only the
parameterssection. Keepprovisioner,reclaimPolicy, andvolumeBindingModeidentical — changing those has consequences beyond disk type. -
Delete and immediately recreate:
-
Verify the new parameters, and that all pods are still Running:
storageclass-backup.yaml.
Step 2: Decommission each bookie in turn
Repeat this section once per bookie. Process from the highest ordinal down to the lowest — for examplebk-4, bk-3, bk-2, bk-1, bk-0. This keeps the ordering consistent with how the StatefulSet would scale in, and avoids repeatedly disturbing the lowest-ordinal pod that administrative commands are typically run against.
Mark the bookie for decommission
The same annotation can be set on the
BookKeeperCluster resource to apply to all bookies, and the pod-level annotation takes precedence over it. Setting the annotation to "false" prevents decommissioning instead — useful as a guard while other maintenance is in progress.Delete the pod
The replacement pod may briefly appear
Pending and unschedulable while its old claims are being removed. The operator deletes the pod a second time to work around a Kubernetes StatefulSet issue in this situation. This is expected and requires no action.Monitor decommission progress
0. On a bookie holding hundreds of gigabytes this can take hours.
Verify the bookie returned to service
Speed up re-replication
Re-replication throughput is limited by the number of auto-recovery workers. If decommissioning is slower than you can accept, you can increase them:Clean up retained volumes
If the StorageClass reclaim policy isRetain, each claim the operator deleted leaves its PersistentVolume behind in the Released phase, along with the disk backing it. Those disks continue to incur cost until you remove them.
Released volume corresponds to a bookie you decommissioned, and that the cluster reports no under-replicated ledgers, before deleting it and its underlying disk.
Troubleshooting
Decommission is taking too long
- Check the number of auto-recovery workers and consider increasing them.
- Check free capacity on the remaining bookies. Replication slows sharply as they fill.
- Check network throughput between bookie nodes.
Decommission stalls with ledgers permanently under-replicated
Auto-recovery cannot place replicas when too few bookies remain. If the bookie count minus one is below the largest configured ensemble size, decommissioning cannot complete. Scale the cluster up before continuing, then retry.The StorageClass change is not reflected in the StatefulSet
sn-operator replaces the StatefulSet only while it is Ready and its replica count is unchanged. Check that all bookie pods are Ready and no scaling operation is in progress.
Also confirm the StatefulSet is not opted out of management or replacement — spec.apiObjects.bookieStatefulSet.managed: false or replaceEnabled: false on the BookKeeperCluster both prevent the templates from being updated. Then check the sn-operator logs.
A replacement pod stays Pending
- Claim is
Pending— nothing provisioned it. Confirm the StorageClass named in the StatefulSet templates exists. - Insufficient capacity — the new volumes could not be provisioned on the available nodes. Check node capacity and any topology constraints on the StorageClass.