> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shiftlabs.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Storage Classes

> Manage Kubernetes StorageClasses for dynamic storage provisioning

StorageClasses define different classes of storage in a cluster. They specify which provisioner to use, what parameters to pass, and how volumes should behave. StorageClasses enable dynamic provisioning of PersistentVolumes.

## Key Concepts

<CardGroup cols={2}>
  <Card title="StorageClass" icon="layers">
    A cluster-scoped template that defines how storage should be provisioned.
  </Card>

  <Card title="Provisioner" icon="settings">
    The volume plugin that creates PVs (e.g., CSI drivers, cloud providers).
  </Card>

  <Card title="Binding Mode" icon="clock">
    When to provision storage: immediately or when a pod needs it.
  </Card>

  <Card title="Default Class" icon="star">
    The StorageClass used when PVCs don't specify one.
  </Card>
</CardGroup>

<Info>
  StorageClasses are **cluster-scoped** resources. They are available to all namespaces and define how storage is provisioned across the entire cluster.
</Info>

## Required Permissions

| Action              | Permission                                     |
| ------------------- | ---------------------------------------------- |
| View StorageClasses | `iam:project:infrastructure:kubernetes:read`   |
| Create StorageClass | `iam:project:infrastructure:kubernetes:write`  |
| Edit StorageClass   | `iam:project:infrastructure:kubernetes:write`  |
| Delete StorageClass | `iam:project:infrastructure:kubernetes:delete` |

## Key Fields

| Field                    | Description                                                |
| ------------------------ | ---------------------------------------------------------- |
| **provisioner**          | The volume plugin that creates PVs (required)              |
| **reclaimPolicy**        | What happens to PV when PVC is deleted (Delete or Retain)  |
| **volumeBindingMode**    | When to bind PV to PVC (Immediate or WaitForFirstConsumer) |
| **allowVolumeExpansion** | Whether PVCs can be resized after creation                 |
| **parameters**           | Provisioner-specific configuration options                 |
| **mountOptions**         | Options passed to the mount command                        |

## Volume Binding Modes

| Mode                     | Description                                             |
| ------------------------ | ------------------------------------------------------- |
| **Immediate**            | PV is provisioned as soon as PVC is created (default)   |
| **WaitForFirstConsumer** | PV is provisioned when a pod using the PVC is scheduled |

<Tip>
  Use **WaitForFirstConsumer** for topology-aware provisioning. This ensures volumes are created in the same availability zone as the pod, preventing scheduling issues.
</Tip>

## How to View StorageClasses

<Steps>
  <Step title="Select Cluster">
    Choose a cluster from the cluster dropdown.
  </Step>

  <Step title="View List">
    The list shows all StorageClasses in the cluster (cluster-scoped, no namespace filter).
  </Step>

  <Step title="Filter and Search">
    Use the search box to find StorageClasses by name or provisioner. Filter by binding mode (Immediate, WaitForFirstConsumer).
  </Step>
</Steps>

## How to View StorageClass Details

<Steps>
  <Step title="Find the StorageClass">
    Locate the StorageClass in the list.
  </Step>

  <Step title="Click StorageClass Name">
    Click on the StorageClass name to open the detail drawer.
  </Step>

  <Step title="Review Details">
    View StorageClass information including:

    * **Overview**: Name, provisioner, reclaim policy, binding mode, age
    * **Features**: Volume expansion, default status
    * **Parameters**: Provisioner-specific configuration
    * **Mount Options**: Mount command options
    * **Allowed Topologies**: Zone/region constraints
    * **Labels & Annotations**: Metadata
  </Step>
</Steps>

## How to Create a StorageClass

<Steps>
  <Step title="Click Create StorageClass">
    Click the **Create StorageClass** button in the page header.
  </Step>

  <Step title="Write YAML">
    Enter the StorageClass manifest in YAML format. Key fields:

    * `provisioner` - Volume plugin identifier (required)
    * `reclaimPolicy` - Delete or Retain
    * `volumeBindingMode` - Immediate or WaitForFirstConsumer
    * `allowVolumeExpansion` - true/false
    * `parameters` - Provisioner-specific options
  </Step>

  <Step title="Create">
    Click **Create** to apply the manifest.
  </Step>
</Steps>

## How to Edit a StorageClass

<Steps>
  <Step title="Open Actions Menu">
    Click the actions menu (three dots) on the StorageClass row.
  </Step>

  <Step title="Click Edit YAML">
    Select **Edit YAML** to open the YAML editor.
  </Step>

  <Step title="Modify Spec">
    Edit the StorageClass specification. Note that most fields are immutable after creation.
  </Step>

  <Step title="Save">
    Click **Update** to apply changes.
  </Step>
</Steps>

<Warning>
  Most StorageClass fields (provisioner, parameters, reclaimPolicy, volumeBindingMode) are **immutable** after creation. To change these, delete and recreate the StorageClass.
</Warning>

## How to Delete a StorageClass

<Steps>
  <Step title="Open Actions Menu">
    Click the actions menu on the StorageClass row.
  </Step>

  <Step title="Click Delete">
    Select **Delete** from the menu.
  </Step>

  <Step title="Confirm">
    Confirm the deletion. Existing PVCs using this class will not be affected.
  </Step>
</Steps>

<Warning>
  Deleting a StorageClass prevents new PVCs from using it for dynamic provisioning. Existing PVCs and PVs are not affected.
</Warning>

## Default StorageClass

One StorageClass can be marked as the cluster default. PVCs that don't specify a `storageClassName` will use the default class.

**Setting the default:**

```yaml theme={null}
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/aws-ebs
```

| Annotation                                         | Description                               |
| -------------------------------------------------- | ----------------------------------------- |
| `storageclass.kubernetes.io/is-default-class`      | Set to `"true"` for default (recommended) |
| `storageclass.beta.kubernetes.io/is-default-class` | Legacy annotation, also supported         |

<Info>
  Only one StorageClass should be marked as default. If multiple are marked, behavior is undefined and may vary by Kubernetes version.
</Info>

## Example StorageClasses

### AWS EBS (CSI)

```yaml theme={null}
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-gp3
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  fsType: ext4
  encrypted: "true"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
```

### Google Cloud Persistent Disk

```yaml theme={null}
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: pd-ssd
provisioner: pd.csi.storage.gke.io
parameters:
  type: pd-ssd
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
```

### Azure Disk

```yaml theme={null}
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: azure-premium
provisioner: disk.csi.azure.com
parameters:
  skuName: Premium_LRS
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
```

### Local Storage

```yaml theme={null}
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
```

### NFS (External Provisioner)

```yaml theme={null}
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client
provisioner: nfs-subdir-external-provisioner
parameters:
  archiveOnDelete: "false"
reclaimPolicy: Delete
volumeBindingMode: Immediate
```

## Common Provisioners

| Provisioner                    | Description                             |
| ------------------------------ | --------------------------------------- |
| `ebs.csi.aws.com`              | AWS EBS CSI driver                      |
| `pd.csi.storage.gke.io`        | Google Cloud Persistent Disk CSI        |
| `disk.csi.azure.com`           | Azure Disk CSI driver                   |
| `file.csi.azure.com`           | Azure File CSI driver                   |
| `csi.vsphere.vmware.com`       | VMware vSphere CSI                      |
| `rook-ceph.rbd.csi.ceph.com`   | Rook Ceph block storage                 |
| `kubernetes.io/no-provisioner` | No dynamic provisioning (local storage) |

## Troubleshooting

<AccordionGroup>
  <Accordion title="PVC stuck in Pending with no provisioner">
    * Verify the StorageClass exists
    * Check provisioner pods are running
    * Ensure CSI driver is properly installed
    * Check for errors in provisioner logs
  </Accordion>

  <Accordion title="Volume provisioned in wrong zone">
    * Use `volumeBindingMode: WaitForFirstConsumer`
    * Configure `allowedTopologies` if needed
    * Verify pod has appropriate node selectors
  </Accordion>

  <Accordion title="Volume expansion not working">
    * Verify `allowVolumeExpansion: true` in StorageClass
    * Check if the provisioner supports expansion
    * Pod restart may be required for filesystem expansion
    * Check PVC conditions for resize status
  </Accordion>

  <Accordion title="Cannot create PVC with StorageClass">
    * Verify StorageClass exists: `kubectl get sc`
    * Check PVC `storageClassName` matches exactly
    * Verify provisioner has necessary permissions
    * Check provisioner logs for errors
  </Accordion>

  <Accordion title="Default StorageClass not being used">
    * Verify annotation is exactly `storageclass.kubernetes.io/is-default-class: "true"`
    * Ensure only one StorageClass is marked as default
    * Check PVC doesn't explicitly set `storageClassName: ""`
  </Accordion>

  <Accordion title="Parameters not taking effect">
    * Parameters are passed directly to the provisioner
    * Check provisioner documentation for valid parameters
    * Verify parameter values are strings (quoted in YAML)
    * Invalid parameters may be silently ignored
  </Accordion>
</AccordionGroup>

## FAQ

<AccordionGroup>
  <Accordion title="What is dynamic provisioning?">
    Dynamic provisioning automatically creates PVs when PVCs are created. The StorageClass specifies the provisioner and parameters used to create the storage. No manual PV creation is needed.
  </Accordion>

  <Accordion title="When should I use WaitForFirstConsumer?">
    Use **WaitForFirstConsumer** when:

    * Using multi-zone clusters
    * Storage must be in the same zone as the pod
    * Using local persistent volumes
    * Topology-aware provisioning is needed

    It delays provisioning until a pod is scheduled, ensuring the volume is created in the correct location.
  </Accordion>

  <Accordion title="Can I change a StorageClass after creation?">
    Most fields are immutable (provisioner, parameters, reclaimPolicy, volumeBindingMode). You can only modify labels, annotations, and allowVolumeExpansion. To change other fields, delete and recreate the StorageClass.
  </Accordion>

  <Accordion title="What happens to existing PVCs if I delete a StorageClass?">
    Existing PVCs and PVs are not affected. They retain their original settings. Only new PVCs cannot use the deleted StorageClass.
  </Accordion>

  <Accordion title="Should I have multiple StorageClasses?">
    Yes, typically. Create different StorageClasses for:

    * Different performance tiers (SSD vs HDD)
    * Different reclaim policies (Delete vs Retain)
    * Different use cases (databases vs logs)
    * Different availability zones or regions
  </Accordion>

  <Accordion title="What is the difference between Delete and Retain reclaim policies?">
    **Delete**: When PVC is deleted, PV and underlying storage are automatically deleted. Good for ephemeral data.

    **Retain**: PV is kept when PVC is deleted, allowing manual data recovery. The PV enters "Released" state.
  </Accordion>

  <Accordion title="How do mount options work?">
    Mount options are passed to the `mount` command when attaching volumes. Common options include `ro` (read-only), `noatime`, `nodiratime`. Not all provisioners support mount options.
  </Accordion>

  <Accordion title="What are allowed topologies?">
    Allowed topologies restrict where volumes can be provisioned based on node labels (typically zones/regions). This ensures volumes are only created in specific locations, useful for compliance or performance requirements.
  </Accordion>
</AccordionGroup>
