{"id":79848,"date":"2026-11-12T05:55:12","date_gmt":"2026-11-12T05:55:12","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=79848"},"modified":"2026-11-12T05:56:32","modified_gmt":"2026-11-12T05:56:32","slug":"kubernetes-storageclass-using-rancher-local-path-provisioner-complete-step-by-step-kubernetes-tutorial","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/kubernetes-storageclass-using-rancher-local-path-provisioner-complete-step-by-step-kubernetes-tutorial\/","title":{"rendered":"Kubernetes storageclass using Rancher Local Path Provisioner: Complete Step-by-Step Kubernetes Tutorial"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Goal:<\/strong>&nbsp;Automatically provision&nbsp;<strong>hostPath-backed PersistentVolumes (PVs)<\/strong>&nbsp;for a&nbsp;<strong>3-replica StatefulSet<\/strong>&nbsp;on a&nbsp;<strong>self-managed Ubuntu Kubernetes cluster<\/strong>, without hand-writing a PV or a PVC manifest.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Lab setting<\/th><th class=\"has-text-align-left\" data-align=\"left\">Value<\/th><\/tr><\/thead><tbody><tr><td>Kubernetes<\/td><td>v1.35.10 (self-managed \/ kubeadm-style installation)<\/td><\/tr><tr><td>Nodes<\/td><td>2 in the original example; one node may be control-plane tainted<\/td><\/tr><tr><td>Operating system<\/td><td>Ubuntu Linux<\/td><\/tr><tr><td>Provisioner<\/td><td>Rancher Local Path Provisioner (<code>rancher.io\/local-path<\/code>)<\/td><\/tr><tr><td>Example installer version<\/td><td><code>v0.0.37<\/code>&nbsp;(upstream documented stable version when checked)<\/td><\/tr><tr><td>Volume backend<\/td><td>Kubernetes&nbsp;<code>hostPath<\/code>&nbsp;(not NFS, EBS, or a&nbsp;<code>local:<\/code>&nbsp;PV)<\/td><\/tr><tr><td>StorageClass<\/td><td><code>local-storage<\/code><\/td><\/tr><tr><td>StatefulSet<\/td><td><code>local-test<\/code>, 3 replicas<\/td><\/tr><tr><td>PVC per replica<\/td><td>1Gi requested;&nbsp;<strong>not a hard disk quota<\/strong><\/td><\/tr><tr><td>Default host data root<\/td><td><code>\/opt\/local-path-provisioner<\/code>&nbsp;on the selected node<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Scope:<\/strong>&nbsp;For the application, you maintain only&nbsp;<strong>two YAML manifests:&nbsp;<code>sc.yaml<\/code>&nbsp;and&nbsp;<code>sfs.yaml<\/code><\/strong>. You still install a cluster-level provisioner once. The provisioner installation itself contains a controller, RBAC permissions, configuration, and helper resources; these cannot be replaced by a StorageClass alone. StatefulSet generates PVC objects, and the provisioner generates PV objects automatically.<\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Safety:<\/strong>&nbsp;This is ideal for labs, development, and certain node-local workloads. It is&nbsp;<strong>not replicated or portable storage<\/strong>, and&nbsp;<code>hostPath<\/code>&nbsp;warrants careful security controls. With&nbsp;<code>reclaimPolicy: Delete<\/code>, deleting a PVC can&nbsp;<strong>delete its data<\/strong>. Do not use the cleanup commands on important workloads.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. Understand the architecture<\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>               Self-managed Kubernetes cluster\n\n  +------------------------------------------------------+\n  | Local Path Provisioner controller (installed once)   |\n  |               rancher.io\/local-path                  |\n  +---------------------------+--------------------------+\n                              |\n                     watches PVC requests\n                              |\n  +---------------------------v--------------------------+\n  | StorageClass: local-storage                           |\n  | provisioner: rancher.io\/local-path                    |\n  | defaultVolumeType: hostPath                           |\n  | volumeBindingMode: WaitForFirstConsumer               |\n  +---------------------------+--------------------------+\n                              |\n                StatefulSet: local-test (3)\n                   \/           |           \\\n               Pod-0         Pod-1         Pod-2\n                 |             |             |\n               PVC-0         PVC-1         PVC-2\n                 |             |             |\n           auto PV-0      auto PV-1      auto PV-2\n                 |             |             |\n          hostPath dir   hostPath dir   hostPath dir\n          on its node    on its node    on its node\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Important distinction:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Provisioner<\/th><th class=\"has-text-align-left\" data-align=\"left\">Dynamic PV creation?<\/th><th class=\"has-text-align-left\" data-align=\"left\">PV backend<\/th><th class=\"has-text-align-left\" data-align=\"left\">Needs an installed controller?<\/th><\/tr><\/thead><tbody><tr><td><code>kubernetes.io\/no-provisioner<\/code><\/td><td><strong>No<\/strong><\/td><td>Existing manually-created PVs<\/td><td>No (but you create PVs yourself)<\/td><\/tr><tr><td><code>rancher.io\/local-path<\/code><\/td><td><strong>Yes<\/strong><\/td><td><code>hostPath<\/code>&nbsp;or&nbsp;<code>local<\/code><\/td><td><strong>Yes<\/strong><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><code>WaitForFirstConsumer<\/code>&nbsp;delays&nbsp;<strong>scheduling-aware binding\/provisioning<\/strong>. It does&nbsp;<strong>not<\/strong>&nbsp;turn&nbsp;<code>kubernetes.io\/no-provisioner<\/code>&nbsp;into a dynamic provisioner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The StatefulSet automatically creates&nbsp;<strong>one PVC per replica<\/strong>. The Rancher controller then creates the corresponding PV and backing directory on the chosen node. A&nbsp;<code>1Gi<\/code>&nbsp;storage request records capacity for Kubernetes scheduling\/accounting, but Rancher Local Path Provisioner does&nbsp;<strong>not<\/strong>&nbsp;enforce a 1Gi filesystem quota.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. Prerequisites<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You need:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A running Ubuntu-based Kubernetes cluster with\u00a0<code>kubectl<\/code>\u00a0configured and cluster-admin rights for installation.<\/li>\n\n\n\n<li>At least one\u00a0<strong>Ready, schedulable worker node<\/strong>. Your earlier cluster has two nodes, and one was excluded by an\u00a0<strong>untolerated taint<\/strong>; avoid using the control-plane node for this lab.<\/li>\n\n\n\n<li>Working cluster networking, DNS, and access to pull images from a container registry.<\/li>\n\n\n\n<li>Available disk space on the worker node under\u00a0<code>\/opt<\/code>\u00a0(or a custom configured storage root).<\/li>\n\n\n\n<li>Your original\u00a0<code>local-test<\/code>\u00a0StatefulSet and\u00a0<code>local-storage<\/code>\u00a0StorageClass are disposable\u00a0<strong>lab resources<\/strong>\u00a0if you follow the replacement steps.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Check your cluster:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl version\nkubectl cluster-info\nkubectl <span class=\"hljs-keyword\">get<\/span> nodes -o wide\nkubectl <span class=\"hljs-keyword\">get<\/span> nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints\nkubectl <span class=\"hljs-keyword\">get<\/span> sc\nkubectl <span class=\"hljs-keyword\">get<\/span> pv\nkubectl <span class=\"hljs-keyword\">get<\/span> pvc -A\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Check available disk space&nbsp;<strong>on each worker node that may store volumes<\/strong>:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">df -h \/opt\n<\/code><\/span><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">A storage request of&nbsp;<code>1Gi<\/code>&nbsp;is&nbsp;<strong>not<\/strong>&nbsp;a disk usage limit with this provisioner. Plan actual disk capacity for all three replicas, logs, images, and node operations.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3. Remove the previous failed lab resources (only if disposable)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Your old StorageClass used&nbsp;<code>kubernetes.io\/no-provisioner<\/code>, and the PVC&nbsp;<code>local-vol-local-test-0<\/code>&nbsp;stayed&nbsp;<code>Pending<\/code>. The StorageClass&nbsp;<code>provisioner<\/code>&nbsp;field cannot be changed in place; recreate that particular class.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Read before running:<\/strong>&nbsp;these commands remove only your&nbsp;<code>local-test<\/code>&nbsp;example and its specifically named claims. Do&nbsp;<strong>not<\/strong>&nbsp;delete unrelated PVs\/PVCs such as&nbsp;<code>deehostpath<\/code>,&nbsp;<code>hostpath<\/code>, or&nbsp;<code>hostpath2<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># Inspect first<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nkubectl get statefulset local-test --ignore-not-found\nkubectl get pvc local-vol-local-test-0 local-vol-local-test-1 local-vol-local-test-2 --ignore-not-found\nkubectl get sc local-storage --ignore-not-found\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># Delete the old StatefulSet, then its example PVCs<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nkubectl delete statefulset local-test --ignore-not-found\nkubectl delete pvc \\\n  local-vol-local-test-0 \\\n  local-vol-local-test-1 \\\n  local-vol-local-test-2 \\\n  --ignore-not-found\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># Recreate the class later with the correct provisioner<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nkubectl delete storageclass local-storage --ignore-not-found\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">If these objects are already absent, skip this step. If they contain valuable data,&nbsp;<strong>do not delete them<\/strong>; use different demo names instead.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4. Install Rancher Local Path Provisioner (once per cluster)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use the versioned upstream manifest rather than the moving&nbsp;<code>master<\/code>&nbsp;branch for a repeatable lab:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl apply -f \\\n  <span class=\"hljs-attr\">https<\/span>:<span class=\"hljs-comment\">\/\/raw.githubusercontent.com\/rancher\/local-path-provisioner\/v0.0.37\/deploy\/local-path-storage.yaml<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This upstream manifest installs the provisioner in namespace&nbsp;<strong><code>local-path-storage<\/code><\/strong>. It also creates its own example\/default-named&nbsp;<strong><code>local-path<\/code><\/strong>&nbsp;StorageClass. We will separately create&nbsp;<strong>our<\/strong>&nbsp;<code>local-storage<\/code>&nbsp;StorageClass next.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Verify installation:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">get<\/span> namespace local-path-storage\nkubectl <span class=\"hljs-keyword\">get<\/span> deploy -n local-path-storage\nkubectl <span class=\"hljs-keyword\">get<\/span> pods -n local-path-storage -o wide\nkubectl rollout status deploy\/local-path-provisioner -n local-path-storage --timeout=180s\nkubectl <span class=\"hljs-keyword\">get<\/span> sc\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Expected: the&nbsp;<code>local-path-provisioner<\/code>&nbsp;Deployment is available and its Pod is&nbsp;<code>Running<\/code>&nbsp;(the Pod suffix will differ).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Check controller logs:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">kubectl logs -n local-path-storage \\\n  -l app=local-path-provisioner --tail=100\n<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">What did that installation create?<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">get<\/span> all -n local-path-storage\nkubectl <span class=\"hljs-keyword\">get<\/span> configmap local-path-config -n local-path-storage\nkubectl <span class=\"hljs-keyword\">get<\/span> sc local-path -o yaml\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The default configuration stores data beneath&nbsp;<strong><code>\/opt\/local-path-provisioner<\/code>&nbsp;on the worker node that receives the Pod<\/strong>. The provisioner creates the per-volume directory automatically; you normally do&nbsp;<strong>not<\/strong>&nbsp;need to create each directory manually.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5. Create the dynamic StorageClass \u2014&nbsp;<code>sc.yaml<\/code><\/h2>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">apiVersion: storage.k8s.io\/v1\nkind: StorageClass\nmetadata:\n  name: local-storage\n  annotations:\n    defaultVolumeType: hostPath\nprovisioner: rancher.io\/local-path\nreclaimPolicy: Delete\nvolumeBindingMode: WaitForFirstConsumer\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Save the YAML above as&nbsp;<strong><code>sc.yaml<\/code><\/strong>, then apply it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl apply -f sc.yaml\nkubectl <span class=\"hljs-keyword\">get<\/span> sc\nkubectl describe sc local-storage\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">What the key fields do:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Field<\/th><th class=\"has-text-align-left\" data-align=\"left\">Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>name: local-storage<\/code><\/td><td>Name referenced in the StatefulSet&#8217;s&nbsp;<code>volumeClaimTemplates<\/code>.<\/td><\/tr><tr><td><code>defaultVolumeType: hostPath<\/code><\/td><td>Rancher-specific StorageClass annotation that requests a&nbsp;<strong>hostPath<\/strong>-backed PV.<\/td><\/tr><tr><td><code>provisioner: rancher.io\/local-path<\/code><\/td><td>Delegates provisioning to the installed Rancher controller.<\/td><\/tr><tr><td><code>reclaimPolicy: Delete<\/code><\/td><td>When a PVC is deleted, the dynamically provisioned PV and its backing directory are normally deleted.<\/td><\/tr><tr><td><code>volumeBindingMode: WaitForFirstConsumer<\/code><\/td><td>Selects an appropriate node for the consuming Pod before creating\/binding node-local storage.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Rancher defaults to&nbsp;<code>hostPath<\/code>&nbsp;if the volume type isn&#8217;t specified, but the explicit annotation makes the lab unambiguous. A PVC-level&nbsp;<code>volumeType<\/code>&nbsp;annotation can override this StorageClass default.<\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Important:<\/strong>&nbsp;This does&nbsp;<strong>not<\/strong>&nbsp;set&nbsp;<code>local-storage<\/code>&nbsp;as the cluster&#8217;s default StorageClass. Your StatefulSet explicitly requests it, which is safer for a lab.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">6. Create the StatefulSet \u2014&nbsp;<code>sfs.yaml<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This matches your original StatefulSet design, but replaces the old BusyBox image location and uses the new dynamically provisioned class.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">apiVersion: apps\/v1\n<span class=\"hljs-attr\">kind<\/span>: StatefulSet\n<span class=\"hljs-attr\">metadata<\/span>:\n  name: local-test\n<span class=\"hljs-attr\">spec<\/span>:\n  serviceName: local-service\n  <span class=\"hljs-attr\">replicas<\/span>: <span class=\"hljs-number\">3<\/span>\n  <span class=\"hljs-attr\">selector<\/span>:\n    matchLabels:\n      app: local-test\n  <span class=\"hljs-attr\">template<\/span>:\n    metadata:\n      labels:\n        app: local-test\n    <span class=\"hljs-attr\">spec<\/span>:\n      containers:\n        - name: test-container\n          <span class=\"hljs-attr\">image<\/span>: busybox:<span class=\"hljs-number\">1.36<\/span>\n          <span class=\"hljs-attr\">command<\/span>:\n            - <span class=\"hljs-regexp\">\/bin\/<\/span>sh\n            - -c\n          <span class=\"hljs-attr\">args<\/span>:\n            - sleep <span class=\"hljs-number\">100000<\/span>\n          <span class=\"hljs-attr\">volumeMounts<\/span>:\n            - name: local-vol\n              <span class=\"hljs-attr\">mountPath<\/span>: <span class=\"hljs-regexp\">\/usr\/<\/span>test-pod\n  <span class=\"hljs-attr\">volumeClaimTemplates<\/span>:\n    - metadata:\n        name: local-vol\n      <span class=\"hljs-attr\">spec<\/span>:\n        accessModes:\n          - ReadWriteOnce\n        <span class=\"hljs-attr\">storageClassName<\/span>: local-storage\n        <span class=\"hljs-attr\">resources<\/span>:\n          requests:\n            storage: <span class=\"hljs-number\">1<\/span>Gi\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Save as&nbsp;<strong><code>sfs.yaml<\/code><\/strong>, and apply:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl apply -f sfs.yaml\nkubectl <span class=\"hljs-keyword\">get<\/span> statefulset local-test\nkubectl <span class=\"hljs-keyword\">get<\/span> pods -o wide -w\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Each Pod gets its own PVC automatically, named:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">local-vol-local-test-0\nlocal-vol-local-test-1\nlocal-vol-local-test-2\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>Why you may see only Pod 0 at first:<\/strong>&nbsp;By default, StatefulSets use ordered creation (<code>OrderedReady<\/code>):&nbsp;<code>local-test-1<\/code>&nbsp;is not created until&nbsp;<code>local-test-0<\/code>&nbsp;becomes Ready, and similarly for Pod 2.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">The StatefulSet references&nbsp;<code>serviceName: local-service<\/code>. For a complete StatefulSet network identity, Kubernetes recommends creating a matching&nbsp;<strong>headless Service<\/strong>. Its absence is&nbsp;<strong>not<\/strong>&nbsp;the cause of your PV\/PVC provisioning error; an optional Service manifest is shown in Section 13. The two main application YAML files remain&nbsp;<code>sc.yaml<\/code>&nbsp;and&nbsp;<code>sfs.yaml<\/code>.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">7. Confirm the three dynamic PVs and PVCs<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">get<\/span> statefulset local-test\nkubectl <span class=\"hljs-keyword\">get<\/span> pods -o wide\nkubectl <span class=\"hljs-keyword\">get<\/span> pvc\nkubectl <span class=\"hljs-keyword\">get<\/span> pv\nkubectl describe pvc local-vol-local-test-0\nkubectl describe pod local-test-0\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Typical outcome (names\/ages may differ):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">$ kubectl get pods\nNAME           READY   STATUS    RESTARTS\nlocal-test-0   1\/1     Running   0\nlocal-test-1   1\/1     Running   0\nlocal-test-2   1\/1     Running   0\n\n$ kubectl get pvc\nNAME                     STATUS   VOLUME       CAPACITY   ACCESS MODES   STORAGECLASS\nlocal-vol-local-test-0   Bound    pvc-<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">uid0<\/span>&gt;<\/span>   1Gi        RWO            local-storage\nlocal-vol-local-test-1   Bound    pvc-<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">uid1<\/span>&gt;<\/span>   1Gi        RWO            local-storage\nlocal-vol-local-test-2   Bound    pvc-<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">uid2<\/span>&gt;<\/span>   1Gi        RWO            local-storage\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The PVs&#8217;&nbsp;<code>.spec.storageClassName<\/code>&nbsp;should be&nbsp;<code>local-storage<\/code>; their claim references should point to the three&nbsp;<code>local-vol-local-test-*<\/code>&nbsp;PVCs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Verify the PV backend is&nbsp;<em>really hostPath<\/em><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;em&gt;<span class=\"hljs-comment\"># Find the PV name for replica 0&lt;\/em&gt;<\/span>\nPV=$(kubectl get pvc local-vol-local-test<span class=\"hljs-number\">-0<\/span> -o jsonpath=<span class=\"hljs-string\">'{.spec.volumeName}'<\/span>)\n<span class=\"hljs-keyword\">echo<\/span> <span class=\"hljs-string\">\"$PV\"<\/span>\n\n&lt;em&gt;<span class=\"hljs-comment\"># Read its hostPath (must be nonempty)&lt;\/em&gt;<\/span>\nkubectl get pv <span class=\"hljs-string\">\"$PV\"<\/span> -o jsonpath=<span class=\"hljs-string\">'{.spec.hostPath.path}{\"\\n\"}'<\/span>\n\n&lt;em&gt;<span class=\"hljs-comment\"># Inspect the full PV object (hostPath and node affinity)&lt;\/em&gt;<\/span>\nkubectl get pv <span class=\"hljs-string\">\"$PV\"<\/span> -o yaml\n\n&lt;em&gt;<span class=\"hljs-comment\"># Display PV backend paths for all PVs (old PVs may have different storage classes)&lt;\/em&gt;<\/span>\nkubectl get pv -o custom-columns=NAME:.metadata.name,STORAGECLASS:.spec.storageClassName,HOSTPATH:.spec.hostPath.path\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The PV will contain a&nbsp;<code>spec.hostPath<\/code>&nbsp;entry resembling:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">spec:\n  hostPath:\n    path: <span class=\"hljs-regexp\">\/opt\/<\/span>local-path-provisioner\/pvc-<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">uid<\/span>&gt;<\/span>_default_local-vol-local-test-0\n    type: DirectoryOrCreate\n<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The actual generated directory name will differ. Inspect the&nbsp;<em>live<\/em>&nbsp;PV to determine the exact path; do not assume the example path is literal.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Verify PV affinity and the selected node<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">get<\/span> pod local-test-0 -o wide\nkubectl <span class=\"hljs-keyword\">get<\/span> pv \"$PV\" -o yaml\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The PV is associated with the node where its data lives, commonly through&nbsp;<code>nodeAffinity<\/code>&nbsp;using&nbsp;<code>kubernetes.io\/hostname<\/code>.&nbsp;<strong>A Pod using this PVC cannot automatically move its data to another node.<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">8. Test persistence inside a replica<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Write a file into the PVC-backed mount:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl exec local-test<span class=\"hljs-number\">-0<\/span> -- sh -c \\\n  <span class=\"hljs-string\">'echo \"Hello from Rancher dynamic hostPath\" &gt; \/usr\/test-pod\/hello.txt'<\/span>\n\nkubectl exec local-test<span class=\"hljs-number\">-0<\/span> -- cat \/usr\/test-pod\/hello.txt\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Expected:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">Hello <span class=\"hljs-keyword\">from<\/span> Rancher dynamic hostPath\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Delete&nbsp;<em>only the Pod<\/em>&nbsp;(not the PVC):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">delete<\/span> pod local-test<span class=\"hljs-number\">-0<\/span>\nkubectl <span class=\"hljs-keyword\">get<\/span> pod local-test-0 -w\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Once it is&nbsp;<code>Running<\/code>&nbsp;and Ready again:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">kubectl exec local-test-0 -- cat \/usr\/test-pod\/hello.txt\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Expected: the same text. This demonstrates persistence across Pod recreation&nbsp;<strong>on its original storage node<\/strong>. It does&nbsp;<strong>not<\/strong>&nbsp;demonstrate survival of worker-node disk failure or data movement between nodes.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">9. Verify separate data for separate replicas<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">for<\/span> i <span class=\"hljs-keyword\">in<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">1<\/span> <span class=\"hljs-number\">2<\/span>; <span class=\"hljs-keyword\">do<\/span>\n  kubectl exec <span class=\"hljs-string\">\"local-test-$i\"<\/span> -- sh -c \\\n    <span class=\"hljs-string\">\"echo Replica-$i &gt; \/usr\/test-pod\/replica.txt\"<\/span>\ndone\n\n<span class=\"hljs-keyword\">for<\/span> i <span class=\"hljs-keyword\">in<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">1<\/span> <span class=\"hljs-number\">2<\/span>; <span class=\"hljs-keyword\">do<\/span>\n  printf <span class=\"hljs-string\">'Pod %s: '<\/span> <span class=\"hljs-string\">\"$i\"<\/span>\n  kubectl exec <span class=\"hljs-string\">\"local-test-$i\"<\/span> -- cat \/usr\/test-pod\/replica.txt\ndone\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Expected:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Pod 0: Replica-0\nPod 1: Replica-1\nPod 2: Replica-2\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Each StatefulSet replica gets its own PVC and PV. Even when the PVs reside on the&nbsp;<strong>same worker<\/strong>, they use different directories.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">10. Locate the data on the Ubuntu worker node<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">First identify the node hosting replica 0, and read its PV&#8217;s path:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">NODE=$(kubectl get pod local-test<span class=\"hljs-number\">-0<\/span> -o jsonpath=<span class=\"hljs-string\">'{.spec.nodeName}'<\/span>)\nPV=$(kubectl get pvc local-vol-local-test<span class=\"hljs-number\">-0<\/span> -o jsonpath=<span class=\"hljs-string\">'{.spec.volumeName}'<\/span>)\nHOST_PATH=$(kubectl get pv <span class=\"hljs-string\">\"$PV\"<\/span> -o jsonpath=<span class=\"hljs-string\">'{.spec.hostPath.path}'<\/span>)\n\nprintf <span class=\"hljs-string\">'Pod node: %s\\nPV name: %s\\nHost path: %s\\n'<\/span> <span class=\"hljs-string\">\"$NODE\"<\/span> <span class=\"hljs-string\">\"$PV\"<\/span> <span class=\"hljs-string\">\"$HOST_PATH\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Now&nbsp;<strong>SSH or use your normal admin access to that specific worker node<\/strong>, then inspect the path printed above:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">sudo ls -lah \/opt\/local-path-provisioner\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Optionally inspect the&nbsp;<strong>exact<\/strong>&nbsp;<code>HOST_PATH<\/code>&nbsp;you printed, after verifying it, on the node where the PV lives:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">sudo ls -lah \/opt\/local-path-provisioner\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">generated-volume-directory<\/span>&gt;<\/span>\/\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">If the command runner is a control-plane host rather than the Pod&#8217;s worker, the directory may&nbsp;<strong>not exist there<\/strong>. Local storage is located on the selected worker, not wherever you execute&nbsp;<code>kubectl<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">11. Understand the storage lifecycle<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Operation<\/th><th class=\"has-text-align-left\" data-align=\"left\">What happens to data?<\/th><\/tr><\/thead><tbody><tr><td>Delete a StatefulSet Pod<\/td><td>StatefulSet recreates it and normally reuses its PVC and node-local data.<\/td><\/tr><tr><td>Scale StatefulSet down<\/td><td>Pods are removed; PVCs are retained by default.<\/td><\/tr><tr><td>Scale it back up<\/td><td>Existing retained PVCs are normally reused.<\/td><\/tr><tr><td>Delete StatefulSet<\/td><td>Existing PVCs remain by default under the default PVC retention policy.<\/td><\/tr><tr><td>Delete a PVC using this class<\/td><td><code>Delete<\/code>&nbsp;reclaim policy means backing PV\/directory are normally removed by the provisioner.&nbsp;<strong>Destructive.<\/strong><\/td><\/tr><tr><td>Worker node\/disk fails<\/td><td>Data is&nbsp;<strong>not replicated<\/strong>; the bound PVC is not transparently portable to another node.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You can explore safe scaling behavior:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl scale statefulset local-test --replicas=<span class=\"hljs-number\">1<\/span>\nkubectl <span class=\"hljs-keyword\">get<\/span> pods\nkubectl <span class=\"hljs-keyword\">get<\/span> pvc  &lt;em&gt;# Claims for replicas 1 and 2 normally remain.&lt;\/em&gt;\n\nkubectl scale statefulset local-test --replicas=3\nkubectl <span class=\"hljs-keyword\">get<\/span> pods -w\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Do not mistake&nbsp;<code>ReadWriteOnce<\/code>&nbsp;for automatic cross-node shared storage. These are&nbsp;<strong>separate local volumes<\/strong>, each with node affinity.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">12. Troubleshooting your exact&nbsp;<code>Pending<\/code>&nbsp;problem<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Your previous scheduler messages included:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">WaitForFirstConsumer\nWaitForPodScheduled\n0\/2 nodes are available:\n  1 node(s) didn't find available persistent volumes to bind,\n  1 node(s) had untolerated taint(s).\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Those messages were consistent with the&nbsp;<strong>old<\/strong>&nbsp;<code>kubernetes.io\/no-provisioner<\/code>&nbsp;class and missing matching static PVs. After switching to the Rancher class, use the checks below.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A. PVC still Pending<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">get<\/span> sc local-storage -o yaml\nkubectl describe pvc local-vol-local-test-0\nkubectl describe pod local-test-0\nkubectl <span class=\"hljs-keyword\">get<\/span> events -A --sort-by=.lastTimestamp | tail -n 40\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Confirm&nbsp;<strong>all<\/strong>&nbsp;of the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>StorageClass provisioner is\u00a0<strong><code>rancher.io\/local-path<\/code><\/strong>,\u00a0<em>not<\/em>\u00a0<code>kubernetes.io\/no-provisioner<\/code>.<\/li>\n\n\n\n<li>The controller Deployment is available in\u00a0<code>local-path-storage<\/code>.<\/li>\n\n\n\n<li><code>local-test-0<\/code>\u00a0is eligible for a Ready worker node.<\/li>\n\n\n\n<li>The PVC references\u00a0<code>storageClassName: local-storage<\/code>.<\/li>\n\n\n\n<li>There are no admission-policy failures or permission errors preventing helper Pods from creating directories.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">B. Provisioner is not Running \/ images fail to pull<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">get<\/span> pods -n local-path-storage -o wide\nkubectl describe deploy local-path-provisioner -n local-path-storage\nkubectl describe pods -n local-path-storage\nkubectl logs -n local-path-storage -l app=local-path-provisioner --tail=200\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Check node internet\/registry access, DNS, proxy settings, and image pull errors. The provisioner uses a helper Pod to set up and remove directories; blocked helper Pods can prevent PVC binding.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">C. Pod cannot schedule because of a node taint<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">get<\/span> nodes -o wide\nkubectl <span class=\"hljs-keyword\">get<\/span> nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints\nkubectl describe pod local-test-0\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>Recommendation:<\/strong>&nbsp;Keep your control-plane taint in place and run this lab on the worker. Do not remove production control-plane taints merely to make the demo succeed. If the only worker is&nbsp;<code>NotReady<\/code>, cordoned, or resource-starved, fix that first.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">D. Pod says ImagePullBackOff<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use the current image from the tutorial:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">image<\/span>: <span class=\"hljs-selector-tag\">busybox<\/span><span class=\"hljs-selector-pseudo\">:1.36<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The original&nbsp;<code>k8s.gcr.io\/busybox<\/code>&nbsp;reference is outdated. Check&nbsp;<code>kubectl describe pod local-test-0<\/code>&nbsp;for exact registry\/network failures.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">E. Volume created on the wrong node \/ directory cannot be found<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">kubectl get pod local-test<span class=\"hljs-number\">-0<\/span> -o wide\nPV=$(kubectl get pvc local-vol-local-test<span class=\"hljs-number\">-0<\/span> -o jsonpath=<span class=\"hljs-string\">'{.spec.volumeName}'<\/span>)\nkubectl get pv <span class=\"hljs-string\">\"$PV\"<\/span> -o yaml\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Examine the PV&#8217;s node affinity and&nbsp;<code>spec.hostPath.path<\/code>. Inspect that path&nbsp;<strong>on the selected worker<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">F. Disk full or permission errors<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">On the selected worker:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">df -h \/opt\nsudo ls -ld \/opt\/local-path-provisioner\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Check provisioner\/helper-Pod logs, node security rules, and your Pod&#8217;s UID\/GID. The default upstream setup script creates the volume directory with permissive permissions; don&#8217;t assume that is suitable for multi-tenant production.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">G.&nbsp;<code>sc.yaml<\/code>&nbsp;fails because the old provisioner is immutable<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A StorageClass&#8217;s&nbsp;<code>provisioner<\/code>&nbsp;field cannot be edited in place. For this disposable lab, remove the old&nbsp;<code>local-test<\/code>&nbsp;StatefulSet and its old PVCs, delete&nbsp;<strong>only<\/strong>&nbsp;StorageClass&nbsp;<code>local-storage<\/code>, then recreate it with&nbsp;<code>rancher.io\/local-path<\/code>&nbsp;(Sections 3\u20135).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">H. Three replicas, only one Pod visible<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">get<\/span> sts local-test\nkubectl describe pod local-test-0\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This is often due to the StatefulSet&#8217;s default&nbsp;<code>OrderedReady<\/code>&nbsp;creation behavior. Fix Pod 0; later replicas should follow.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">13. Optional: add the StatefulSet&#8217;s headless Service<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Not required for proving dynamic PV creation<\/strong>, but recommended if you want correct per-Pod DNS and normal StatefulSet network identity.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">apiVersion: v1\nkind: Service\nmetadata:\n  name: local-service\nspec:\n  clusterIP: None\n  selector:\n    app: local-test\n  ports:\n    - name: http\n      port: 80\n      targetPort: 80\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Save as&nbsp;<code>headless-service.yaml<\/code>, then:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl apply -f headless-service.yaml\nkubectl <span class=\"hljs-keyword\">get<\/span> svc local-service\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The BusyBox lab container does not listen on port 80; the example Service exists for StatefulSet identity, not application HTTP traffic. If you require&nbsp;<strong>strictly two YAML resources<\/strong>, you may omit this optional Service for the&nbsp;<strong>storage-only exercise<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">14. Optional: customize the host data directory<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The installer default is&nbsp;<code>\/opt\/local-path-provisioner<\/code>. To see the live setting:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">kubectl get configmap local-path-config -n local-path-storage \\\n  -o jsonpath=<span class=\"hljs-string\">'{.data.config\\.json}{\"\\n\"}'<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The config contains&nbsp;<code>nodePathMap<\/code>, for example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">{\n  <span class=\"hljs-attr\">\"nodePathMap\"<\/span>: &#91;\n    {\n      <span class=\"hljs-attr\">\"node\"<\/span>: <span class=\"hljs-string\">\"DEFAULT_PATH_FOR_NON_LISTED_NODES\"<\/span>,\n      <span class=\"hljs-attr\">\"paths\"<\/span>: &#91;<span class=\"hljs-string\">\"\/opt\/local-path-provisioner\"<\/span>]\n    }\n  ]\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">If you want a different host directory, such as&nbsp;<code>\/mnt\/k8s-local-data<\/code>, first ensure that path is available on all eligible workers, and edit the provisioner&#8217;s ConfigMap:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">kubectl edit configmap local-path-config -n local-path-storage\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Change&nbsp;<strong>only<\/strong>&nbsp;the&nbsp;<code>paths<\/code>&nbsp;entry inside its&nbsp;<code>config.json<\/code>&nbsp;to your desired existing\/accessible parent path. The provisioner supports configuration reload, but existing PVs remain on their original paths. For a simple first lab,&nbsp;<strong>keep the default location<\/strong>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Do not choose&nbsp;<code>\/<\/code>&nbsp;as a provisioning root. Ensure your worker disks, mount points, permissions, backup plan, and node selection are appropriate before using any custom path.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">15. Cleanup the lab safely<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Destructive warning:<\/strong>&nbsp;Because&nbsp;<code>local-storage<\/code>&nbsp;uses&nbsp;<code>reclaimPolicy: Delete<\/code>, deleting your PVCs can remove their PVs&nbsp;<strong>and stored files<\/strong>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># 1. Stop the application<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nkubectl delete statefulset local-test --ignore-not-found\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># 2. Remove only this lab's claims \u2014 THIS DELETES THE DATA<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nkubectl delete pvc \\\n  local-vol-local-test-0 \\\n  local-vol-local-test-1 \\\n  local-vol-local-test-2 \\\n  --ignore-not-found\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># 3. Verify the associated PVs disappear<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nkubectl get pv\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># 4. Remove our additional StorageClass<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nkubectl delete sc local-storage --ignore-not-found\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># 5. Only if you created the optional headless Service<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\nkubectl delete svc local-service --ignore-not-found\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">If you also want to&nbsp;<strong>uninstall the provisioner itself<\/strong>, first verify no other PVC\/PV\/StorageClass depends on&nbsp;<code>rancher.io\/local-path<\/code>. Then remove the&nbsp;<strong>same versioned upstream manifest<\/strong>&nbsp;used to install it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">kubectl <span class=\"hljs-keyword\">delete<\/span> -f \\\n  <span class=\"hljs-attr\">https<\/span>:<span class=\"hljs-comment\">\/\/raw.githubusercontent.com\/rancher\/local-path-provisioner\/v0.0.37\/deploy\/local-path-storage.yaml<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This removes cluster-level installed resources (including the upstream&nbsp;<code>local-path<\/code>&nbsp;class). Do not uninstall it if other workloads still depend on it.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">16. Complete command sequence (quick reference)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For a&nbsp;<strong>fresh, disposable<\/strong>&nbsp;lab, after checking cluster access and preparing&nbsp;<code>sc.yaml<\/code>&nbsp;and&nbsp;<code>sfs.yaml<\/code>&nbsp;as in Sections 5\u20136:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-32\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;em&gt;<span class=\"hljs-comment\"># Check nodes \/ taints&lt;\/em&gt;<\/span>\nkubectl get nodes -o wide\nkubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints\n\n&lt;em&gt;<span class=\"hljs-comment\"># Install the dynamic provisioner (once)&lt;\/em&gt;<\/span>\nkubectl apply -f \\\n  https:<span class=\"hljs-comment\">\/\/raw.githubusercontent.com\/rancher\/local-path-provisioner\/v0.0.37\/deploy\/local-path-storage.yaml<\/span>\nkubectl rollout status deploy\/local-path-provisioner -n local-path-storage --timeout=<span class=\"hljs-number\">180<\/span>s\n\n&lt;em&gt;<span class=\"hljs-comment\"># If replacing the old, disposable objects from your original failed lab:&lt;\/em&gt;<\/span>\nkubectl delete sts local-test --ignore-not-found\nkubectl delete pvc local-vol-local-test<span class=\"hljs-number\">-0<\/span> local-vol-local-test<span class=\"hljs-number\">-1<\/span> local-vol-local-test<span class=\"hljs-number\">-2<\/span> --ignore-not-found\nkubectl delete sc local-storage --ignore-not-found\n\n&lt;em&gt;<span class=\"hljs-comment\"># Apply your TWO application YAML files&lt;\/em&gt;<\/span>\nkubectl apply -f sc.yaml\nkubectl apply -f sfs.yaml\n\n&lt;em&gt;<span class=\"hljs-comment\"># Verify automatic PV \/ PVC creation&lt;\/em&gt;<\/span>\nkubectl get sc\nkubectl get sts\nkubectl get pods -o wide\nkubectl get pvc\nkubectl get pv\n\n&lt;em&gt;<span class=\"hljs-comment\"># Confirm backend is hostPath&lt;\/em&gt;<\/span>\nPV=$(kubectl get pvc local-vol-local-test<span class=\"hljs-number\">-0<\/span> -o jsonpath=<span class=\"hljs-string\">'{.spec.volumeName}'<\/span>)\nkubectl get pv <span class=\"hljs-string\">\"$PV\"<\/span> -o jsonpath=<span class=\"hljs-string\">'{.spec.hostPath.path}{\"\\n\"}'<\/span>\n\n&lt;em&gt;<span class=\"hljs-comment\"># Verify persistence after Pod recreation&lt;\/em&gt;<\/span>\nkubectl exec local-test<span class=\"hljs-number\">-0<\/span> -- sh -c <span class=\"hljs-string\">'echo works &gt; \/usr\/test-pod\/check.txt'<\/span>\nkubectl delete pod local-test<span class=\"hljs-number\">-0<\/span>\nkubectl wait --<span class=\"hljs-keyword\">for<\/span>=condition=Ready pod\/local-test<span class=\"hljs-number\">-0<\/span> --timeout=<span class=\"hljs-number\">180<\/span>s\nkubectl exec local-test<span class=\"hljs-number\">-0<\/span> -- cat \/usr\/test-pod\/check.txt\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-32\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>Success criteria:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>kubectl get pods<\/code>\u00a0shows\u00a0<code>local-test-0<\/code>,\u00a0<code>local-test-1<\/code>, and\u00a0<code>local-test-2<\/code>\u00a0as\u00a0<code>Running<\/code>\u00a0and\u00a0<code>Ready<\/code>.<\/li>\n\n\n\n<li>All three\u00a0<code>local-vol-local-test-*<\/code>\u00a0PVCs are\u00a0<code>Bound<\/code>.<\/li>\n\n\n\n<li>Kubernetes has automatically created three PVs with StorageClass\u00a0<code>local-storage<\/code>.<\/li>\n\n\n\n<li>Each PV has a nonempty\u00a0<strong><code>spec.hostPath.path<\/code><\/strong>.<\/li>\n\n\n\n<li>A file written under\u00a0<code>\/usr\/test-pod<\/code>\u00a0remains after deleting\/recreating its Pod.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">17. Frequently asked questions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q: Can&nbsp;<code>kubernetes.io\/no-provisioner<\/code>&nbsp;create dynamic hostPath PVs?<\/strong><br>No. Its name literally means it will not provision PVs. You must create matching PVs manually.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q: Can&nbsp;<code>WaitForFirstConsumer<\/code>&nbsp;make it dynamic?<\/strong><br>No. It only controls the timing of binding\/provisioning where a provisioner exists.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q: Is Rancher itself or a Rancher-managed Kubernetes cluster required?<\/strong><br>No. The&nbsp;<strong>Local Path Provisioner<\/strong>&nbsp;can run as a component in a self-managed upstream Kubernetes cluster. You don&#8217;t need to install the Rancher management platform.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q: Can I use a single StorageClass with multiple StatefulSet replicas?<\/strong><br>Yes. Each replica&#8217;s PVC uses the same class, but gets its own separate PV\/directory.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q: Does&nbsp;<code>1Gi<\/code>&nbsp;prevent the Pod from writing more than 1Gi?<\/strong><br>No. The Rancher Local Path Provisioner currently doesn&#8217;t enforce the requested capacity as a hard quota.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q: Can the storage follow a Pod to another node?<\/strong><br>No. With a local hostPath volume, PV node affinity ties the volume to its node. Consider NFS CSI, Longhorn, or a replicated\/distributed CSI solution if portability or redundancy is required.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q: Why didn&#8217;t my original&nbsp;<code>hostpath2<\/code>&nbsp;PV bind to the PVC?<\/strong><br>Its&nbsp;<code>storageClassName<\/code>&nbsp;was&nbsp;<code>manual<\/code>, not&nbsp;<code>local-storage<\/code>; additionally, the old&nbsp;<code>local-storage<\/code>&nbsp;class had no dynamic provisioner. PV matching checks StorageClass as well as capacity, access modes, volume mode, and scheduling constraints.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Q: Is this suitable for production databases?<\/strong><br>Only after evaluating node failure, disk health, backups, recovery, quotas, permission boundaries, monitoring, and availability requirements. For production-grade replicated storage, assess a storage system designed for that need.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">18. Official references<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Rancher Local Path Provisioner (README, installation, volume types, configuration, limitations):\u00a0<a href=\"https:\/\/github.com\/rancher\/local-path-provisioner\">https:\/\/github.com\/rancher\/local-path-provisioner<\/a><\/li>\n\n\n\n<li>Rancher Local Path Provisioner releases:\u00a0<a href=\"https:\/\/github.com\/rancher\/local-path-provisioner\/releases\">https:\/\/github.com\/rancher\/local-path-provisioner\/releases<\/a><\/li>\n\n\n\n<li>Versioned install manifest (<code>v0.0.37<\/code>):\u00a0<a href=\"https:\/\/raw.githubusercontent.com\/rancher\/local-path-provisioner\/v0.0.37\/deploy\/local-path-storage.yaml\">https:\/\/raw.githubusercontent.com\/rancher\/local-path-provisioner\/v0.0.37\/deploy\/local-path-storage.yaml<\/a><\/li>\n\n\n\n<li>Kubernetes StatefulSet volume claim templates:\u00a0<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/controllers\/statefulset\/\">https:\/\/kubernetes.io\/docs\/concepts\/workloads\/controllers\/statefulset\/<\/a><\/li>\n\n\n\n<li>Kubernetes StorageClasses and volume binding modes:\u00a0<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/storage\/storage-classes\/\">https:\/\/kubernetes.io\/docs\/concepts\/storage\/storage-classes\/<\/a><\/li>\n\n\n\n<li>Kubernetes PersistentVolumes:\u00a0<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/storage\/persistent-volumes\/\">https:\/\/kubernetes.io\/docs\/concepts\/storage\/persistent-volumes\/<\/a><\/li>\n\n\n\n<li>Kubernetes local and hostPath volumes:\u00a0<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/storage\/volumes\/\">https:\/\/kubernetes.io\/docs\/concepts\/storage\/volumes\/<\/a><\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Tutorial prepared for Kubernetes v1.35.10 on a self-managed Ubuntu cluster. Confirm the current stable Rancher release when repeating this installation later; pin the installer version for reproducibility.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Goal:&nbsp;Automatically provision&nbsp;hostPath-backed PersistentVolumes (PVs)&nbsp;for a&nbsp;3-replica StatefulSet&nbsp;on a&nbsp;self-managed Ubuntu Kubernetes cluster, without hand-writing a PV or a PVC manifest. Lab setting Value Kubernetes v1.35.10 (self-managed \/ kubeadm-style installation)&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-79848","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/79848","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=79848"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/79848\/revisions"}],"predecessor-version":[{"id":79849,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/79848\/revisions\/79849"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=79848"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=79848"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=79848"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}