<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>PVC · XB Field Notes</title><link>https://hk.xbcatm.com/tags/pvc/</link><description>Illustrated field notes on HTTP, DNS, TLS, Caddy, SSH, releases, and recovery.</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://hk.xbcatm.com/tags/pvc/index.xml" rel="self" type="application/rss+xml"/><item><title>A PVC gives a Pod durable storage beyond the container</title><link>https://hk.xbcatm.com/posts/kubernetes-pvc-storage-lifecycle/</link><pubDate>Fri, 13 Aug 2027 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/posts/kubernetes-pvc-storage-lifecycle/</guid><description>A PersistentVolumeClaim binds to a backing volume and stays across Pod restarts, while recovery still depends on the backing storage and its topology.</description><content:encoded>&lt;p&gt;A PersistentVolumeClaim separates storage lifetime from a recreated Pod; it does not guarantee data recovery. A PersistentVolume describes storage, a claim requests it, and controllers arrange binding or provisioning. Recovery still depends on backing storage, usable topology and backups.&lt;/p&gt;
&lt;p&gt;Claims and volumes have separate lifetimes. Finalizers can postpone deletion, while reclaim policy controls the released storage. Review both before maintenance: mistakes can cause persistent Terminating states, unintended data loss or retained storage bills.&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/posts-kubernetes-pvc-storage-lifecycle.detail.en.svg" alt="Pod, claim and volume are separate objects. Binding requires matching conditions. Protection can delay deletion; after claim deletion, a supporting plugin asynchronously reclaims the PV and storage under Delete, while Retain leaves Released storage for deliberate reclamation." width="560" height="2960"&gt;&lt;figcaption&gt;Protection delays deletion; it does not establish controller health. Retain requires an ownership, backup and provider-procedure decision. Retained diagnostics remain blocked and were not rerun.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="the-claim-the-volume-and-the-binding"&gt;The claim, the volume, and the binding&lt;/h2&gt;
&lt;p&gt;Ordinary matching checks capacity, access modes, storage class, volumeMode, selectors and existing ownership. An unmatched claim can remain Pending; inspect its events and StorageClass rather than assuming a single cause. Binding is not proof of usable topology: explicitly naming a PV can bind despite conflicting node affinity. Scheduling and mounting still have to succeed.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;accessModes&lt;/code&gt; has no default. &lt;code&gt;ReadWriteOnce&lt;/code&gt; permits read-write mounting on one node, potentially by several Pods there; it is not single-Pod exclusivity. &lt;code&gt;ReadWriteOncePod&lt;/code&gt; provides that constraint for supported CSI volumes. Access modes alone do not enforce write protection after mounting. &lt;code&gt;Immediate&lt;/code&gt; starts binding or provisioning without waiting for a consumer, risking unusable placement for topology-constrained storage. Supported &lt;code&gt;WaitForFirstConsumer&lt;/code&gt; provisioning coordinates storage with a consuming Pod’s scheduling constraints before placement completes.&lt;/p&gt;
&lt;h2 id="the-deletion-order-and-the-finalizer"&gt;The deletion order and the finalizer&lt;/h2&gt;
&lt;p&gt;A usual retirement sequence is Pod, claim, then volume. Deleting an in-use claim records deletionTimestamp, but &lt;code&gt;kubernetes.io/pvc-protection&lt;/code&gt; can postpone removal. In Kubernetes v1.34.0, the ordinary PVC-reference check counts Pods assigned to a node; an unscheduled Pod reference alone does not hold protection. This is a controller reference check, not a live mount probe. A stuck Terminating claim therefore needs inspection of scheduled consumers, controller progress and other finalizers.&lt;/p&gt;
&lt;p&gt;The same protection exists in the other direction. A &lt;code&gt;PersistentVolume&lt;/code&gt; carries &lt;code&gt;kubernetes.io/pv-protection&lt;/code&gt;, so deleting a volume that is bound to a claim is postponed until it is no longer bound. That ordering delays an accidental deletion; a deletionTimestamp cannot simply be cancelled. This explains why a volume can seem to ignore a delete for as long as its claim exists.&lt;/p&gt;
&lt;h2 id="reclaim-policy-delete-or-retain"&gt;Reclaim policy: Delete or Retain&lt;/h2&gt;
&lt;p&gt;When the claim is finally gone the volume is released, and the reclaim policy takes over. With a supporting storage plugin, &lt;code&gt;Delete&lt;/code&gt; reclaims the PV and backing asset asynchronously; removal is not an atomic or immediate guarantee. &lt;code&gt;Retain&lt;/code&gt; keeps the PV object, marks it &lt;code&gt;Released&lt;/code&gt;, and deliberately does not make it available for a new claim, because the previous claimant&amp;rsquo;s data is still on it — reclamation becomes a manual sequence: verify ownership and backups, then deliberately reclaim, reuse or delete storage according to the provider&amp;rsquo;s procedure.&lt;/p&gt;
&lt;p&gt;Dynamically provisioned volumes inherit the policy from their StorageClass, whose default is &lt;code&gt;Delete&lt;/code&gt;. Whether that default fits a database depends on retention and recovery requirements. It is easy to overlook: nothing in the PVC or pod spec mentions the policy, so the decision lives in a cluster-scoped object nobody reviewing the workload ever sees.&lt;/p&gt;
&lt;h2 id="when-it-breaks"&gt;When it breaks&lt;/h2&gt;
&lt;p&gt;The classic failure is the claim that will not die. Someone deletes a PVC, sees &lt;code&gt;Terminating&lt;/code&gt;, and waits — while a pod somewhere still mounts it, usually a StatefulSet that was never scaled down or a pod stuck on an unreachable node. Nothing errors, nothing times out, and the PVC blocks the storage behind it. Find the pod rather than deleting the finalizer by hand; bypassing protection can permit premature storage deletion while a workload still uses it.&lt;/p&gt;
&lt;p&gt;The second failure is the Retain orphan. The claim is gone, the pods are gone, the namespace may be gone, and a &lt;code&gt;Released&lt;/code&gt; PV plus its storage asset remain for deliberate reclamation. Retention may be intentional; without inventory alerts it is easy to miss and the volume does not appear in any workload&amp;rsquo;s list. The inverse mistake is just as quiet: a class whose default &lt;code&gt;Delete&lt;/code&gt; policy was never overridden, discovered only when someone tries to recover data that the reclaim already removed.&lt;/p&gt;
&lt;h2 id="check-it-quickly"&gt;Check it quickly&lt;/h2&gt;
&lt;p&gt;Look at both sides of the binding, and at what holds the claim:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl get pvc -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,STATUS:.status.phase,VOLUME:.spec.volumeName,CLASS:.spec.storageClassName
kubectl get pv -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,POLICY:.spec.persistentVolumeReclaimPolicy,CLAIM:.spec.claimRef.name
kubectl get pvc data -o jsonpath=&amp;#39;{.metadata.finalizers}{&amp;#34;\n&amp;#34;}&amp;#39;
kubectl get pods -A -o json | jq -r &amp;#39;.items[] | select(.spec.volumes[]?.persistentVolumeClaim.claimName==&amp;#34;data&amp;#34;) | &amp;#34;\(.metadata.namespace)/\(.metadata.name)&amp;#34;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These retained diagnostics were not rerun here. Protection indicates cleanup is pending; it does not prove the controller is healthy or a Pod still exists. The fourth command finds all claims named data across namespaces, so match the target namespace before treating a Pod as a consumer. Released plus Retain calls for an ownership and recovery decision, not automatic deletion; the old claimRef may remain.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/"&gt;Persistent Volumes&lt;/a&gt;, binding, access modes, reclaim policies, &lt;code&gt;Released&lt;/code&gt; state and the pvc/pv protection finalizers, checked 2026-10-15.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/storage-classes/"&gt;Storage Classes&lt;/a&gt;, dynamic provisioning, &lt;code&gt;reclaimPolicy&lt;/code&gt;, and &lt;code&gt;volumeBindingMode&lt;/code&gt;, checked 2026-10-15.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://v1-34.docs.kubernetes.io/docs/concepts/storage/persistent-volumes/"&gt;Persistent Volumes (v1.34)&lt;/a&gt;, v1.34 prebinding, access-mode limits and supported reclaim behavior, checked 2026-10-18.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/kubernetes/kubernetes/v1.34.0/pkg/controller/volume/pvcprotection/pvc_protection_controller.go"&gt;PVC protection implementation&lt;/a&gt;, v1.34.0 podUsesPVC node-assignment and explicit-reference checks, checked 2026-10-18.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>