title: How should I deploy Persistent Volume(PV) for JupyterHub on Kubernetes?
body: <p>Environment information:</p>
  <pre><code>Computer detail: One master node and four slave nodes. All are CentOS Linux release 7.8.2003 (Core).
  Kubernetes version: v1.18.0.
  Zero to JupyterHub version: 0.9.0.
  Helm version: v2.11.0
  </code></pre>
  <p>I recently try to deploy an online code environment(like Google Colab) in new lab servers via Zero to JupyterHub. Unfortunately, I failed to deploy Persistent Volume(PV) for JupyterHub and I got a failure message such below:</p>
  <pre><code>Events:
    Type     Reason            Age                From               Message
    ----     ------            ----               ----               -------
    Warning  FailedScheduling  4s (x27 over 35m)  default-scheduler  running &quot;VolumeBinding&quot; filter plugin for pod &quot;hub-7b9cbbcf59-747jl&quot;: pod has unbound immediate PersistentVolumeClaims
  </code></pre>
  <p>I followed the installing process by the <a href="https://zero-to-jupyterhub.readthedocs.io/en/latest/setup-jupyterhub/setup-jupyterhub.html" rel="nofollow noreferrer">tutorial of JupyterHub</a>, and I was used Helm to install JupyterHub on k8s. That config file such below:</p>
  <p><code>config.yaml</code></p>
  <pre class="lang-yaml prettyprint-override"><code>proxy:
    secretToken: &quot;2fdeb3679d666277bdb1c93102a08f5b894774ba796e60af7957cb5677f40706&quot;
  singleuser:
    storage:
      dynamic:
        storageClass: local-storage
  </code></pre>
  <p>Here, I was config a <code>local-storage</code> for JupyterHub, the <code>local-storage</code> was observed k8s:  <a href="https://kubernetes.io/docs/concepts/storage/storage-classes/#local" rel="nofollow noreferrer">Link</a>. And its <code>yaml</code> file
  such like that:</p>
  <pre class="lang-yaml prettyprint-override"><code>apiVersion: storage.k8s.io/v1
  kind: StorageClass
  metadata:
    name: local-storage
  provisioner: kubernetes.io/no-provisioner
  volumeBindingMode: WaitForFirstConsumer
  </code></pre>
  <p>Then I use <code>kubectl get storageclass</code> to check it work, I got the message below:</p>
  <pre><code>NAME            PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
  local-storage   kubernetes.io/no-provisioner   Delete          WaitForFirstConsumer   false                  64m
  </code></pre>
  <p>So, I thought I deployed a storage for JupyterHub, but I so naive. I am so disappointed about that because my other Pods(JupyterHub) are all running. And I have been search some solutions so long, but also failed.</p>
  <p>So now, my problems are:</p>
  <ol>
  <li><p>What is the true way to solve the PV problems? (Better using local storage.)</p>
  </li>
  <li><p>Is the local storage way will using other nodes disk not only master?</p>
  </li>
  <li><p>In fact, my lab had a could storage service, so if Q2 answer is No, and how I using my lab could storage service to deploy PV?</p>
  </li>
  </ol>
  <hr />
  <p>I had been addressed above problem with @Arghya Sadhu's solution. But now, I got a new problem is the Pod <code>hub-db-dir</code> also pending, it result my service <code>proxy-public</code> pending.</p>
  <p>The description of <code>hub-db-dir</code> such below:</p>
  <pre><code>Name:           hub-7b9cbbcf59-jv49z
  Namespace:      jhub
  Priority:       0
  Node:           &lt;none&gt;
  Labels:         app=jupyterhub
                  component=hub
                  hub.jupyter.org/network-access-proxy-api=true
                  hub.jupyter.org/network-access-proxy-http=true
                  hub.jupyter.org/network-access-singleuser=true
                  pod-template-hash=7b9cbbcf59
                  release=jhub
  Annotations:    checksum/config-map: c20a64c7c9475201046ac620b057f0fa65ad6928744f7d265bc8705c959bce2e
                  checksum/secret: 1beaebb110d06103988476ec8a3117eee58d97e7dbc70c115c20048ea04e79a4
  Status:         Pending
  IP:
  IPs:            &lt;none&gt;
  Controlled By:  ReplicaSet/hub-7b9cbbcf59
  Containers:
    hub:
      Image:      jupyterhub/k8s-hub:0.9.0
      Port:       8081/TCP
      Host Port:  0/TCP
      Command:
        jupyterhub
        --config
        /etc/jupyterhub/jupyterhub_config.py
        --upgrade-db
      Requests:
        cpu:      200m
        memory:   512Mi
      Readiness:  http-get http://:hub/hub/health delay=0s timeout=1s period=10s #success=1 #failure=3
      Environment:
        PYTHONUNBUFFERED:        1
        HELM_RELEASE_NAME:       jhub
        POD_NAMESPACE:           jhub (v1:metadata.namespace)
        CONFIGPROXY_AUTH_TOKEN:  &lt;set to the key 'proxy.token' in secret 'hub-secret'&gt;  Optional: false
      Mounts:
        /etc/jupyterhub/config/ from config (rw)
        /etc/jupyterhub/cull_idle_servers.py from config (rw,path=&quot;cull_idle_servers.py&quot;)
        /etc/jupyterhub/jupyterhub_config.py from config (rw,path=&quot;jupyterhub_config.py&quot;)
        /etc/jupyterhub/secret/ from secret (rw)
        /etc/jupyterhub/z2jh.py from config (rw,path=&quot;z2jh.py&quot;)
        /srv/jupyterhub from hub-db-dir (rw)
        /var/run/secrets/kubernetes.io/serviceaccount from hub-token-vlgwz (ro)
  Conditions:
    Type           Status
    PodScheduled   False
  Volumes:
    config:
      Type:      ConfigMap (a volume populated by a ConfigMap)
      Name:      hub-config
      Optional:  false
    secret:
      Type:        Secret (a volume populated by a Secret)
      SecretName:  hub-secret
      Optional:    false
    hub-db-dir:
      Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
      ClaimName:  hub-db-dir
      ReadOnly:   false
    hub-token-vlgwz:
      Type:        Secret (a volume populated by a Secret)
      SecretName:  hub-token-vlgwz
      Optional:    false
  QoS Class:       Burstable
  Node-Selectors:  &lt;none&gt;
  Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                   node.kubernetes.io/unreachable:NoExecute for 300s
  Events:
    Type     Reason            Age                 From               Message
    ----     ------            ----                ----               -------
    Warning  FailedScheduling  61s (x43 over 56m)  default-scheduler  0/5 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 4 node(s) didn't find available persistent volumes to bind.
  
  </code></pre>
  <p>The information with <code>kubectl get pv,pvc,sc</code>.</p>
  <pre><code>NAME                               STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS    AGE
  persistentvolumeclaim/hub-db-dir   Pending                                      local-storage   162m
  
  NAME                                                  PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
  storageclass.storage.k8s.io/local-storage (default)   kubernetes.io/no-provisioner   Delete          WaitForFirstConsumer   false                  8h
  
  </code></pre>
  <p>So, how to fix it?</p>