Every StorageClass on the cluster in one table. Use it to check which provisioner backs a class, whether it can grow, and what happens to the data when a PVC is deleted. ## The classes | Class | Provisioner | Access modes | Binding | Reclaim | Expandable | Backed by | | -------------------- | ------------------- | ------------- | -------------------- | ------- | ---------- | ------------------------------------- | | `standard` (default) | `csi.hetzner.cloud` | RWO | WaitForFirstConsumer | Retain | Yes | Hetzner Cloud volumes, cloud nodes | | `local-nvme` | `topolvm.io` | RWO | WaitForFirstConsumer | Retain | Yes | NVMe disks on bare metal | | `local-ssd` | `topolvm.io` | RWO | WaitForFirstConsumer | Retain | Yes | SATA SSD disks on bare metal | | `local-hdd` | `topolvm.io` | RWO | WaitForFirstConsumer | Retain | Yes | HDD disks on bare metal | | `juicefs` | `csi.juicefs.com` | RWX, RWO, ROX | Immediate | Retain | Yes | Object storage plus a metadata engine | `standard` is present and set as default on every cluster. The `local-*` classes appear once you [install TopoLVM](/docs/hetzner/apalla/storage/local/local-nvme-with-topolvm), and `juicefs` appears once you [install the JuiceFS CSI driver](/docs/hetzner/apalla/storage/shared/readwritemany-with-juicefs). List what your cluster has right now: ```console $ kubectl get storageclasses NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE local-hdd topolvm.io Retain WaitForFirstConsumer true 10d local-nvme topolvm.io Retain WaitForFirstConsumer true 10d local-ssd topolvm.io Retain WaitForFirstConsumer true 10d standard (default) csi.hetzner.cloud Retain WaitForFirstConsumer true 10d ``` ## Reading the columns **Access modes** decide how many nodes can mount a volume. The block and local classes are ReadWriteOnce; only `juicefs` offers ReadWriteMany. See [Access modes and volume binding](/docs/hetzner/apalla/storage/access-modes). **Binding** is when the volume is created. WaitForFirstConsumer waits for a pod so the volume lands where the pod runs. Immediate creates it as soon as the PVC exists, which suits object-backed storage that has no location to match. **Reclaim is Retain on every class, on purpose.** Deleting a PVC deletes the Kubernetes object, not the data behind it: the underlying volume is kept, not erased. Syself would rather a mistaken delete leave your data intact than let one command wipe it. The flip side is that cleanup is a deliberate act. A Hetzner Cloud volume keeps costing money until you delete it yourself, and a local volume keeps holding pool space until you remove it. See [Volume attach constraints and reclaim policies](/docs/hetzner/apalla/storage/block/attach-constraints-and-reclaim) and [Local PV lifecycle](/docs/hetzner/apalla/storage/local/local-pv-lifecycle) for how to reclaim the space when you mean to. > [!IMPORTANT] > Deleting a PVC or PV never deletes the data on its own. If you truly want the data gone, delete the underlying Hetzner Cloud volume or remove the local logical volume as a separate, intentional step. **Expandable** means you can grow a volume by editing the PVC. Every class allows expansion. Volumes only grow, they never shrink. See [Expand a Hetzner Cloud volume](/docs/hetzner/apalla/storage/block/expand-a-volume). **Backed by** is the storage behind each class. `standard` is Hetzner Cloud volumes on cloud nodes: each volume sits on Ceph with three copies, so losing a disk does not lose the data. Hetzner attaches at most 16 volumes to one server, so plan for that ceiling on a volume-heavy node. The `local-*` classes are disks inside one bare metal server and carry no built-in redundancy, so keep the data replicated in the app. ## The default class and how to change it A PVC with no `storageClassName` uses the default class, marked `(default)` in the list above. The default is the one carrying this annotation: ```console $ kubectl get storageclass standard -o jsonpath='{.metadata.annotations.storageclass\.kubernetes\.io/is-default-class}' true ``` To make a different class the default, clear the flag on the current one and set it on the new one: Clear the flag on the current default ```console title="Clear the current default" $ kubectl patch storageclass standard -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}' ``` Set the flag on the new class ```console title="Set the new default" $ kubectl patch storageclass local-nvme -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' ``` > [!TIP] > Prefer naming the class explicitly in each PVC (`storageClassName: local-nvme`) over relying on the default. It makes the intent obvious and survives a change to which class is default.