Introduction
When working with Kubernetes PersistentVolumes, YAML configuration errors are among the most common issues administrators face. One frequently encountered error involves the PersistentVolumeReclaimPolicy field, where incorrect casing or placement triggers a strict decoding error that prevents volume creation.
This guide walks through the error, explains why it happens, shows the correct configuration, and covers PersistentVolume best practices.
The Error
When you apply a PersistentVolume YAML file with incorrect field casing, Kubernetes returns:
Error from server (BadRequest): error when creating "nfs.pv.yaml":
PersistentVolume in version "v1" cannot be handled as a PersistentVolume:
strict decoding error: unknown field "spec.PersistentVolumeReclaimPolicy"
Root Cause
The error occurs because Kubernetes YAML fields are case-sensitive. The correct field name is persistentVolumeReclaimPolicy (camelCase starting with lowercase p), not PersistentVolumeReclaimPolicy (PascalCase starting with uppercase P).
Common mistakes that trigger this error:
| Incorrect | Correct |
|---|---|
spec.PersistentVolumeReclaimPolicy | spec.persistentVolumeReclaimPolicy |
spec.AccessModes | spec.accessModes |
spec.Capacity | spec.capacity |
spec.StorageClassName | spec.storageClassName |
Kubernetes API uses strict decoding by default, meaning any unrecognized field (including incorrectly cased ones) will be rejected.
Correct PersistentVolume Configuration
NFS PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
labels:
type: nfs
environment: production
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs-storage
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /exports/data
server: nfs-server.example.com
hostPath PersistentVolume (Development)
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
hostPath:
path: /mnt/data
type: DirectoryOrCreate
AWS EBS PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
name: ebs-pv
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: gp3
awsElasticBlockStore:
volumeID: vol-0123456789abcdef0
fsType: ext4
Understanding Reclaim Policies
The persistentVolumeReclaimPolicy determines what happens to the volume when its PersistentVolumeClaim (PVC) is deleted:
| Policy | Behavior | Use Case |
|---|---|---|
Retain | Volume is kept, data preserved. Admin must manually clean up. | Production data, databases |
Delete | Volume and data are deleted automatically. | Temporary storage, dev environments |
Recycle | Volume data is scrubbed (rm -rf /volume/*), volume made available again. Deprecated. | Legacy systems only |
Recommendation: Use Retain for production workloads and Delete for ephemeral environments.
Matching PersistentVolumeClaim
A PersistentVolumeClaim (PVC) binds to a PV that matches its requirements:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: nfs-storage
selector:
matchLabels:
type: nfs
The PVC binds to a PV when:
accessModesmatchstorageClassNamematches- Requested storage ≤ PV capacity
- Label selectors match (if specified)
Using the PVC in a Pod
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: nginx:latest
volumeMounts:
- mountPath: /usr/share/nginx/html
name: web-storage
volumes:
- name: web-storage
persistentVolumeClaim:
claimName: nfs-pvc
Creating PersistentVolumes with Ansible
Automate PV creation with the kubernetes.core.k8s module:
- name: Create NFS PersistentVolume
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: PersistentVolume
metadata:
name: "nfs-pv-{{ item.name }}"
labels:
app: "{{ item.name }}"
spec:
capacity:
storage: "{{ item.size }}"
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: "/exports/{{ item.name }}"
server: "{{ nfs_server }}"
loop:
- { name: 'app-data', size: '10Gi' }
- { name: 'logs', size: '5Gi' }
- { name: 'backups', size: '50Gi' }
Troubleshooting PersistentVolume Issues
PVC Stuck in Pending State
kubectl describe pvc my-pvc
Common causes:
- No PV matches the PVC's
storageClassName - Requested storage exceeds available PV capacity
- Access modes don't match
- Label selectors don't match any PV
PV Stuck in Released State
After a PVC is deleted, a Retain PV enters Released state and won't bind to new PVCs:
# Remove the claimRef to make it Available again
kubectl patch pv my-pv -p '{"spec":{"claimRef": null}}'
Validating YAML Before Applying
Use --dry-run to catch errors before they affect your cluster:
kubectl apply -f nfs.pv.yaml --dry-run=server
Or validate the YAML schema:
kubectl apply -f nfs.pv.yaml --validate=true
Common YAML Field Reference
All fields under spec for a PersistentVolume (note the camelCase):
spec:
capacity: # Required
storage: 10Gi
accessModes: # Required
- ReadWriteOnce # RWO, ROX, RWX, or RWOP
persistentVolumeReclaimPolicy: # Retain, Delete, or Recycle
storageClassName: "" # Empty string = no class
volumeMode: Filesystem # Filesystem or Block
mountOptions: # Mount flags
- hard
- nfsvers=4.1
nodeAffinity: # For local volumes
required:
nodeSelectorTerms:
- matchExpressions: [...]
Best Practices
- Always use
--dry-run=serverbefore applying PV configurations - Use
Retainin production — prevents accidental data loss - Label your PVs — makes it easier to match with PVCs via selectors
- Use StorageClasses — for dynamic provisioning instead of manual PV creation
- Monitor PV status — set up alerts for
ReleasedorFailedvolumes - Use Ansible for consistency — automate PV creation across environments
- Validate YAML casing — use a linter like
kubevalorkubeconform
Related Articles
- Create Kubernetes Namespace with Ansible
- Create Kubernetes Service with Ansible
- Create Kubernetes Pod with Ansible
- Keeping Kubernetes Clusters Up to Date
- Configure a Pod Volume for Storage
- Ansible for Kubernetes by Example
- Indentation Errors in Ansible
Conclusion
The spec.PersistentVolumeReclaimPolicy error is caused by incorrect field casing in your YAML configuration. Kubernetes API fields are camelCase — persistentVolumeReclaimPolicy, not PersistentVolumeReclaimPolicy.
The fix is straightforward: correct the casing and validate with --dry-run=server before applying. For production environments, automate PV creation with Ansible and use StorageClasses for dynamic provisioning whenever possible.
Related guide
Related reading: Ansible-driven Kubernetes operations covers this in real-world detail.