PermissionError: [Errno 13] Permission denied: '/home/jovyan/.local'

I am experiencing a “Permission Denied” error when attempting to mount a local NFS (Network File System) directory to the notebook directory in a container using SwarmSpawner for JupyterHub. The goal is to provide persistent storage for user data by utilizing an NFS share. I use jupyterhub:3 for build the image.

jupyterhub_config.py

# Configuration file for jupyterhub.
import os
import sys
from dockerspawner import DockerSpawner
from dockerspawner import SwarmSpawner
from oauthenticator.generic import GenericOAuthenticator
import logging

c = get_config()

class CustomSwarmSpawner(SwarmSpawner):
    def _options_form_default(self):
        cpu_options = [1, 2]  # Possible values for CPUs
        memory_options = [1, 2, 4]  # Possible values for memory in GB
        form_template = """
        <div class="form-group">
            <label for="stack">Choose an image</label>
            <select id="stacks" class="form-control" name="stack">
                <option value="jupyter/base-notebook:2023-04-27">base-notebook</option>
                <option value="jupyter/scipy-notebook:2023-01-30">scipy-notebook</option>
            </select>
        </div>
        <label for="cpu_limit">CPUs:</label>
        <select class="form-control" name="cpu_limit" id="cpu_limit">
            {cpu_options}
        </select>
        <label for="memory_limit">Memory (GiB):</label>
        <select class="form-control" name="memory_limit" id="memory_limit">
            {memory_options}
        </select>
        """
        # Populate the CPU options
        cpu_select = "\n".join([f"<option>{cpu}</option>" for cpu in cpu_options])
        # Populate the memory options
        memory_select = "\n".join([f"<option>{memory}</option>" for memory in memory_options])

        # Render the final form
        options_form = form_template.format(cpu_options=cpu_select, memory_options=memory_select)
        return options_form

    def options_from_form(self, formdata):
        options = {}
        options['stack'] = formdata.get('stack', [''])[0].strip()
        options['mem_limit'] = formdata.get('memory_limit', [''])[0].strip() + "G"
        options['cpu_limit'] = int(formdata.get('cpu_limit', [''])[0].strip())
        self.image = options['stack']
        self.extra_placement_spec = { 'constraints' : ['node.role==worker'] }
        self.mem_limit = options['mem_limit']
        self.cpu_limit = options['cpu_limit']
        return options
        
def create_dir_hook(SwarmSpawner):
    username = SwarmSpawner.user.name  # get the username
    volume_path = os.path.join('/mnt/nfs/jupyterhub/volumes', username)
    
    if not os.path.exists(volume_path):
        os.makedirs(volume_path, 0o755)
    
    mounts_user = [
        {
            'type': 'bind',
            'source': volume_path,
            'target': '/home/jovyan'
        }
    ]
    SwarmSpawner.environment = {'GRANT_SUDO': 'yes'}
    SwarmSpawner.extra_container_spec = {'mounts': mounts_user}
    SwarmSpawner.notebook_dir = '/home/jovyan'


c.JupyterHub.authenticator_class = GenericOAuthenticator
c.GenericOAuthenticator.client_id = 'jupyter'
c.GenericOAuthenticator.client_secret = 'ELTSI1XMbfuvUwNVP98evacc4bStZhhu'
c.GenericOAuthenticator.token_url = 'https://vm02gpu.labs247.id:8083/auth/realms/yavai/protocol/openid-connect/token'
c.GenericOAuthenticator.userdata_url = 'https://vm02gpu.labs247.id:8083/auth/realms/yavai/protocol/openid-connect/userinfo'
c.GenericOAuthenticator.userdata_params = {'state': 'state'}
c.GenericOAuthenticator.username_claim = 'preferred_username'
c.GenericOAuthenticator.login_service = 'Keycloak'
c.GenericOAuthenticator.scope = ['openid', 'profile']
c.GenericOAuthenticator.oauth_callback_url = 'https://172.20.3.60:8333/hub/oauth_callback'
c.JupyterHub.authenticator_class.login_handler._OAUTH_AUTHORIZE_URL = 'https://vm02gpu.labs247.id:8083/auth/realms/yavai/protocol/openid-connect/auth'
c.LocalAuthenticator.create_system_users = True
c.GenericOAuthenticator.allow_all = True
c.GenericOAuthenticator.allow_existing_users = True
c.Authenticator.auto_login = True

c.JupyterHub.ssl_key = '/home/deniy/certApps/labs247.key'
c.JupyterHub.ssl_cert = '/home/deniy/certApps/labs247.crt'
 
c.JupyterHub.spawner_class = CustomSwarmSpawner
c.Spawner.pre_spawn_hook = create_dir_hook
c.JupyterHub.hub_ip = '0.0.0.0'
c.JupyterHub.hub_connect_ip = 'hub'
network_name = os.environ['DOCKER_NETWORK_NAME']
c.SwarmSpawner.network_name = network_name
#notebook_dir = '/home/jovyan/work'
#c.SwarmSpawner.notebook_dir = notebook_dir
c.SwarmSpawner.http_timeout = 300
c.SwarmSpawner.start_timeout = 300
c.SwarmSpawner.remove_containers = True
c.ResourceUseDisplay.track_cpu_percent = True
c.Spawner.cmd = ['start.sh', 'jupyter', 'labhub']
c.SwarmSpawner.debug = True
c.JupyterHub.log_level = logging.DEBUG

c.JupyterHub.load_roles = [
    {
        "name": "cull-idle-role",
        "scopes": [
            "list:users",
            "read:users:activity",
            "read:servers",
            "read:metrics",
            "delete:servers",
            "access:servers"
            # "admin:users", # if using --cull-users
        ],
        # assignment of role's permissions to:
        "services": ["cull-idle"],
        "users": ['demo'],
    }
]

c.JupyterHub.services = [
    {
        'name': 'cull-idle',
        'command': [sys.executable, 'cull_idle_servers.py', '--timeout=3600'],
    }
]

error logs at service:

I need help.