# Kubernetes Persistentvolume

> Implements v1 PersistentVolume, StorageClass, and PersistentVolumeClaim manifests with static/dynamic provisioning, access modes, and reclaim policies for Kubernetes storage.

- Skill: `paulpas/kubernetes-persistentvolume` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paulpas/kubernetes-persistentvolume`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paulpas/kubernetes-persistentvolume/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: paulpas (https://skillmd.com/u/paulpas)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/paulpas/kubernetes-persistentvolume

---


# Kubernetes PersistentVolume Manager

Implements v1 PersistentVolume (PV), StorageClass, and PersistentVolumeClaim (PVC) manifests for block and file storage provisioning in Kubernetes. When loaded, the model generates production-grade storage manifests with proper access modes, reclaim policies, and storage class configuration for both static and dynamic provisioning.

## TL;DR Checklist

- [ ] Use `v1` API version for PersistentVolume, PersistentVolumeClaim, and `storage.k8s.io/v1` for StorageClass
- [ ] Match PVC `accessModes` to the PV's supported access modes — mismatch causes Pending PVC state
- [ ] Set `reclaimPolicy: Retain` for production databases, `Delete` for ephemeral workloads
- [ ] Specify `storageClassName` explicitly — omitting it uses the default class or fails binding
- [ ] Use dynamic provisioning (StorageClass) for most workloads — static provisioning requires manual PV creation
- [ ] Never use `ReadWriteMany` with block storage — only filesystem-based volumes support it

---

## When to Use

Use this skill when:

- Provisioning persistent storage for stateful applications (databases, message brokers)
- Configuring storage classes with different performance tiers (SSD, HDD, NVMe)
- Implementing static provisioning with manually created PersistentVolumes
- Setting up reclaim policies for data lifecycle management
- Managing cross-namespace PVC-to-PV binding for multi-tenant clusters

---

## When NOT to Use

Avoid this skill for:

- Stateless application storage needs — use ephemeral `emptyDir` or `hostPath` volumes
- Temporary or scratch data that can be recreated — use `emptyDir` volumes
- Storage that does not require Kubernetes PV/PVC lifecycle management
- Direct cloud provider storage management — use cloud provider SDKs or tools instead

---

## Core Workflow

1. **Select Storage Type and Class** — Determine whether to use dynamic provisioning (StorageClass) or static provisioning (manual PV creation). **Checkpoint:** Verify the cluster has a provisioner configured for the desired storage backend (e.g., `kubernetes.io/aws-ebs`, `ebs.csi.aws.com`).

2. **Create StorageClass (if Dynamic)** — Define a `storage.k8s.io/v1` StorageClass with `provisioner`, `reclaimPolicy`, and parameters. **Checkpoint:** Ensure the provisioner string matches an installed CSI driver or in-tree provisioner.

3. **Create PersistentVolumeClaim** — Define a `v1` PVC with `accessModes`, `resources.requests.storage`, and `storageClassName`. **Checkpoint:** The PVC's `storageClassName` must match an available StorageClass or a manually created PV.

4. **Verify Binding** — Confirm the PVC transitions to `Bound` state. **Checkpoint:** Run `kubectl get pvc -n <namespace>` and verify the `STATUS` column shows `Bound`.

5. **Reference in Pod Spec** — Mount the bound PVC in a pod using a `volumeMount` referencing the PVC name. **Checkpoint:** The PVC `accessModes` must be compatible with the pod's access requirements (e.g., `ReadWriteOnce` pods cannot share `ReadWriteMany` volumes).

6. **Configure Data Lifecycle** — Set `reclaimPolicy` on the StorageClass or PV to control data retention on deletion. **Checkpoint:** For production databases, use `reclaimPolicy: Retain` to prevent accidental data loss.

---

## Implementation Patterns

### Pattern 1: Complete Dynamic and Static Provisioning Setup

Production-grade StorageClass and PersistentVolumeClaim manifests for both provisioning approaches.

```yaml
# StorageClass: High-performance SSD for production databases
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: premium-ssd
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  fsType: ext4
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# StorageClass: Standard storage for development and test environments
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard-hdd
provisioner: ebs.csi.aws.com
parameters:
  type: gp2
  fsType: ext4
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: false
---
# PersistentVolumeClaim: High-performance storage for production database
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data
  namespace: production
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: premium-ssd
  resources:
    requests:
      storage: 100Gi
  volumeMode: Filesystem
---
# PersistentVolumeClaim: Standard storage for application logs
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-logs
  namespace: production
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: nfs-storage
  resources:
    requests:
      storage: 10Gi
  volumeMode: Filesystem
---
# Pod: Mounting the PVCs in a StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: production
spec:
  serviceName: postgres-headless
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    spec:
      containers:
        - name: postgres
          image: postgres:16-alpine
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: postgres-data
```

### Pattern 2: Static Provisioning and Access Mode Matching (BAD vs GOOD)

Incorrect access mode specification or PV/PVC mismatch causes the classic `Pending` PVC state.

```yaml
# ❌ BAD: PVC requests ReadWriteMany but the storage class only supports ReadWriteOnce
# This PVC will remain in Pending state forever.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: broken-pvc
spec:
  accessModes:
    - ReadWriteMany  # ← Most CSI drivers and cloud block storage only support RWO
  storageClassName: ebs-csi
  resources:
    requests:
      storage: 50Gi

# ❌ BAD: ReclaimPolicy: Delete on production database storage — data is lost on PVC deletion
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: prod-storage
reclaimPolicy: Delete  # ← Destructive for production workloads
volumeBindingMode: Immediate

# ❌ BAD: Static PV with mismatched access mode
# The PV supports ReadWriteOnce but the PVC requests ReadWriteOnce — they match,
# but if the PVC asked for ReadWriteMany, it would never bind.
apiVersion: v1
kind: PersistentVolume
metadata:
  name: static-pv
spec:
  capacity:
    storage: 50Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: manual
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /data/static
  # ❌ hostPath is node-local — this PV can only bind to a pod on the same node

# ✅ GOOD: Access modes correctly specified for block storage
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: correct-pvc
spec:
  accessModes:
    - ReadWriteOnce  # ← Matches standard block storage capabilities
  storageClassName: premium-ssd
  resources:
    requests:
      storage: 100Gi
  volumeMode: Filesystem
---
# ✅ GOOD: Static PV with NFS for multi-pod sharing
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-static-pv
spec:
  capacity:
    storage: 50Gi
  accessModes:
    - ReadWriteMany  # ← NFS supports multiple readers/writers
  storageClassName: nfs-storage
  persistentVolumeReclaimPolicy: Retain
  nfs:
    server: nfs-server.example.com
    path: /exports/data
---
# ✅ GOOD: Volume expansion enabled for growing storage needs
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: expandable-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: premium-ssd
  resources:
    requests:
      storage: 50Gi  # ← Will expand to 100Gi when PVC is updated
  volumeMode: Filesystem
---
# To expand: update the PVC's storage request, then verify the PV expands
# kubectl patch pvc expandable-pvc -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'
```

### Pattern 3: Storage Class Parameter Reference

```python
def select_storage_class(
    workload_type: str,
    performance_tier: str,
    required_access_mode: str
) -> str:
    """Select the appropriate StorageClass for a given workload.

    Maps workload requirements to the most suitable storage class in the cluster.

    Args:
        workload_type: Type of workload ('database', 'log', 'temp', 'backup').
        performance_tier: Performance requirement ('premium', 'standard', 'budget').
        required_access_mode: Required access mode ('ReadWriteOnce', 'ReadWriteMany', 'ReadOnlyMany').

    Returns:
        The name of the recommended StorageClass.
    """
    # Database workloads always need premium storage with retain policy
    if workload_type == "database":
        if performance_tier == "premium":
            return "premium-ssd"
        return "standard-hdd"

    # Log storage supports read-many for multi-pod aggregation
    if workload_type == "log":
        if required_access_mode == "ReadWriteMany":
            return "nfs-storage"
        return "standard-hdd"

    # Temporary storage can be deleted on reclaim
    if workload_type == "temp":
        return "ephemeral-ssd"

    # Backup requires retain policy for data safety
    if workload_type == "backup":
        return "premium-ssd"

    raise ValueError(f"Unknown workload type: {workload_type}")
```

---

## Constraints

### MUST DO
- Always specify `storageClassName` explicitly in PVCs — omitting it relies on the default class which may not be appropriate
- Match PVC `accessModes` to the StorageClass/PV capabilities — use `ReadWriteOnce` for block storage and `ReadWriteMany` only for NFS or distributed filesystems
- Set `reclaimPolicy: Retain` on StorageClasses used for production database storage — prevents accidental data loss
- Use `volumeBindingMode: WaitForFirstConsumer` for most workloads — delays PV binding until a pod is scheduled, enabling topology-aware provisioning
- Use `allowVolumeExpansion: true` on StorageClasses to allow PVCs to grow without migration
- Specify `volumeMode: Filesystem` (default) or `volumeMode: Block` explicitly based on the application's needs
- Verify PV/PVC binding with `kubectl get pv,pvc -n <namespace>` after creation

### MUST NOT DO
- Never use `hostPath` volumes for production workloads — they are node-local and survive node failure
- Never set `reclaimPolicy: Delete` on storage used for databases or any data that must be preserved
- Never request `ReadWriteMany` on block storage providers (EBS, Azure Disk, GCE PD) — they only support `ReadWriteOnce`
- Never create a PVC with a `storageClassName` that does not exist in the cluster — it will fail binding immediately
- Never use `volumeBindingMode: Immediate` with zone-sensitive storage — it may bind to a PV in a different zone than the pod
- Never assume a PVC is bound — always check `kubectl get pvc` status before relying on the storage

---

## Output Template

When implementing Kubernetes persistent storage, produce the following:

1. **StorageClass YAML** — StorageClass with provisioner, parameters, reclaim policy, and volume binding mode.
2. **PersistentVolumeClaim YAML** — PVC with access modes, storage requests, and storage class reference.
3. **PersistentVolume YAML** (if static) — Manually created PV with capacity, access modes, and storage backend details.
4. **Pod Volume Mount** — The container spec showing how the PVC is mounted and accessed.

---

## Related Skills

| Skill | Purpose |
|---|---|
| `kubernetes-statefulset` | Use PVCs in StatefulSet volumeClaimTemplates for ordered persistent storage per pod |
| `kubernetes-deployment` | Reference PVCs in Deployment pods for shared persistent storage (single-writer access) |
| `cncf/crossplane` | Manage storage provisioning declaratively with Crossplane composite resources |
| `cncf/longhorn` | Use Longhorn as a distributed block storage solution with native Kubernetes integration |

---

## Live References

> Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.

- [Kubernetes Storage Documentation](https://kubernetes.io/docs/concepts/storage/) — Official guide to PV, PVC, StorageClass, and storage concepts
- [Persistent Volumes Documentation](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) — PV lifecycle, provisioning, and binding behavior
- [Storage Classes Documentation](https://kubernetes.io/docs/concepts/storage/storage-classes/) — StorageClass parameters, provisioners, and reclaim policies
- [Persistent Volume Claim](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims) — PVC binding rules and access mode matching
- [CSI Driver Development](https://kubernetes-csi.github.io/docs/) — Container Storage Interface driver documentation
- [Volume Expansion](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#expanding-persistent-volumes-claims) — Growing PVC storage without data migration
- [StorageClass API Reference — storage.k8s.io/v1](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#storageclass-v1-storage-k8s-io) — Complete API schema for StorageClass resources

