Intro
Managing stateful applications on Kubernetes requires careful handling of Persistent Volume Claims (PVCs). Automating PVC lifecycle in CI/CD pipelines can reduce manual errors, speed up deployments, and ensure consistent environments. However, automation introduces risks: deleting the wrong PVC can cause data loss, and misconfigured storage classes can lead to unschedulable pods. This guide provides a practical, step-by-step approach to integrating PVC management into your CI/CD pipelines, with concrete commands, YAML snippets, and recovery strategies.
We will cover version and environment inventory, safe configuration paths, verification and diagnostics, failure modes, and an operations checklist. Each section includes real-world examples and pitfalls to avoid. The goal is operational safety: observe before changing, limit blast radius, use placeholders instead of secrets, verify results, and document recovery procedures.
Version and Environment Inventory
Before automating PVC operations, you must know your Kubernetes environment. Identify the cluster version, storage classes, and existing PVCs. Use the following commands to gather information:
# Kubernetes server version
kubectl version --short
# List storage classes
kubectl get storageclass
# List all PVCs in all namespaces
kubectl get pvc --all-namespaces
# Check default storage class
kubectl get storageclass -o jsonpath='{.items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")].metadata.name}'
Expected output for kubectl get storageclass might look like:
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
standard (default) kubernetes.io/gce-pd Delete Immediate false 30d
fast kubernetes.io/gce-pd Delete Immediate true 10d
Record the current state before making changes. For example, save a snapshot of PVCs:
kubectl get pvc --all-namespaces -o yaml > pvc-backup-$(date +%Y%m%d).yaml
Prerequisites: Ensure kubectl is installed and configured for the target cluster. Verify you have permissions to create, update, and delete PVCs. Check if you can access the cluster:
kubectl cluster-info
Pitfall: Not checking the default storage class. If no default is set, PVCs without a storage class will remain pending. Verify with:
kubectl get storageclass -o jsonpath='{.items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")].metadata.name}'
If empty, set a default:
kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
Owner: The platform engineer or DevOps lead should be accountable for maintaining the environment inventory. Review this inventory monthly or after any cluster upgrade.
Safe Configuration Path
A safe configuration path for PVC automation involves using version-controlled YAML manifests and applying them through CI/CD. Start with a minimal PVC manifest:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
namespace: production
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard
Apply it using kubectl in a dry-run mode first to validate:
kubectl apply -f pvc.yaml --dry-run=client
If no errors, apply for real:
kubectl apply -f pvc.yaml
Check the PVC status:
kubectl get pvc app-data -n production
Expected output when bound:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
app-data Bound pvc-12345678-abcd-1234-abcd-1234567890ab 5Gi RWO standard 10s
Pitfall: Using a non-existent storage class. If you specify storageClassName: fast but it doesn't exist, the PVC stays Pending. Always list storage classes beforehand. Recover by correcting the manifest or creating the storage class.
Pitfall: Forgetting to set resource requests. Without a size, the PVC may bind to an unexpected size. Always specify storage requests.
In CI/CD, define a pipeline step to apply PVC manifests only after validation. Example GitLab CI snippet:
deploy_pvc:
stage: deploy
script:
- kubectl apply -f pvc.yaml --dry-run=client
- kubectl apply -f pvc.yaml
- kubectl wait --for=condition=Bound pvc/app-data -n production --timeout=60s
only:
- main
The kubectl wait command ensures the PVC is bound before proceeding.
Owner: The application team lead owns the PVC manifest changes. Changes should be reviewed via pull request and merged only after CI passes.
Verification and Diagnostics
After applying a PVC, verify it is bound and attached to the intended pod. Use these commands:
# Get PVC details
kubectl describe pvc app-data -n production
# Check events for PVC
kubectl get events -n production --field-selector involvedObject.name=app-data --sort-by=.metadata.creationTimestamp
# Verify the pod using the PVC
kubectl get pods -n production -o json | jq '.items[] | select(.spec.volumes[].persistentVolumeClaim.claimName=="app-data") | .metadata.name'
Expected output of kubectl describe pvc should show Status: Bound and Volume: pvc-....
If the PVC is Pending, diagnose:
kubectl describe pvc app-data -n production
Look for events like Failed to provision volume with StorageClass "standard" or No PersistentVolume available for this claim.
Pitfall: Access mode mismatch. If PVC requests ReadWriteMany but storage class only supports ReadWriteOnce, provisioning fails. Fix by changing accessModes or using a different storage class.
Pitfall: Insufficient quota. If namespace has resource quotas, the PVC may be rejected. Check quotas:
kubectl get resourcequota -n production
Adjust quota or PVC size.
Automated verification in pipeline:
kubectl wait --for=condition=Bound pvc/app-data -n production --timeout=60s && echo "PVC bound successfully" || (echo "PVC not bound" && exit 1)
Owner: The DevOps engineer on call is responsible for diagnosing PVC issues during deployment. Document diagnostic steps in runbooks.
Failure Modes and Recovery
Common failure modes for PVC automation:
- PVC stuck in Pending: Caused by no matching storage class, insufficient capacity, or unsupported access mode. Recovery:
- Check storage class availability:
kubectl get storageclass - Describe PVC for events:
kubectl describe pvc <name> -n <namespace> - Adjust manifest and reapply.
- Data loss due to reclaim policy Delete: If storage class reclaim policy is Delete, deleting a PVC deletes the underlying volume. To avoid accidental data loss, set reclaim policy to Retain for important data or create a backup before deletion:
- Patch storage class:
kubectl patch storageclass standard -p '{"reclaimPolicy":"Retain"}'(only possible if supported by provisioner). - Before deleting PVC, snapshot the PersistentVolume: check cloud provider snapshot options.
- Pod can't mount volume after PVC recreation: If PVC is deleted and recreated, it may get a new volume, losing data. Instead, keep the same PVC name and use volume cloning or restore from backup.
- Rollback of deployment with PVC: Rolling back a Deployment does not roll back PVC changes if PVC is external to Deployment. Use separate PVC manifests and manage lifecycle independently. If you need to revert storage size or class, create a new PVC and migrate data.
Recovery example: Recreating a wrongly deleted PVC
If PVC was accidentally deleted and reclaim policy is Retain, the underlying PV still exists but is in Released state. To recover:
# Find the PV that was bound to the PVC (check old PV name)
kubectl get pv
# If PV is Released, edit it to remove claimRef
kubectl edit pv <pv-name> # remove spec.claimRef
# Then recreate PVC with same name and specs
kubectl apply -f pvc.yaml
# PVC should bind to the existing PV
Pitfall: Not backing up data before PVC deletion. Always ensure backup policy is in place for stateful applications.
Owner: The database or storage administrator owns recovery procedures. Recovery drills should be performed quarterly.
Operations Checklist
Use this checklist before and after running PVC automation in CI/CD:
- [ ] Confirm cluster version and storage classes are compatible with PVC manifest.
- [ ] Backup existing PVC data if any risk of deletion.
- [ ] Validate manifest with
kubectl apply --dry-run=client. - [ ] Apply PVC and wait for Bound condition.
- [ ] Verify application pod mounts the volume correctly.
- [ ] Monitor for alerts (e.g., PVC usage nearing capacity).
- [ ] Document changes in change management system.
- [ ] Schedule quarterly review of PVC sizes and storage classes.
Owner: The release manager ensures all checklist items are completed. The checklist is reviewed after every production deployment.
Common Pitfalls and How to Avoid Them
Here is a summary of pitfalls beyond those mentioned:
- Using default storage class unknowingly: If you omit
storageClassName, Kubernetes uses the default. This may differ across environments. Always specify storage class explicitly in manifests. - Hardcoding PVC names in multiple places: Pod templates may reference PVC names. If PVC name changes, pods fail. Use Helm values or Kustomize to manage names consistently.
- Ignoring capacity limits: PVC size is fixed; resizing depends on storage class allowVolumeExpansion. Check
ALLOWVOLUMEEXPANSIONin storage class. To resize, patch PVC:
kubectl patch pvc app-data -n production -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'
This works only if expansion is allowed.
- Not setting resource quotas: Without quotas, a runaway process can create many PVCs, consuming storage. Define ResourceQuota in namespace.
- Forgetting to clean up unused PVCs: Monitor and delete PVCs not used by any pod to save costs, but verify first.
Conclusion
Automating Kubernetes PVC management in CI/CD requires careful planning to avoid data loss and downtime. By following the practices in this guide—starting with environment inventory, applying manifests safely, verifying thoroughly, and preparing for failures—you can achieve reliable stateful application deployments. Always version your manifests, test in dry-run, and document recovery steps.
As a next step, implement a simple PVC automation pipeline for a test application. Monitor its behavior, then gradually expand to production with appropriate safeguards. Review your PVC usage quarterly and adjust storage classes and sizes as needed. With these measures, you can confidently manage persistent storage in your Kubernetes clusters.