# Spawn single-user notebook on Kubernetes cluster using kubespawner

**URL:** <https://discourse.jupyter.org/t/spawn-single-user-notebook-on-kubernetes-cluster-using-kubespawner/15569>\
**Category:** Zero to JupyterHub on Kubernetes\
**Tags:** jupyterhub, how-to\
**Created:** [August 30, 2022, 9:35pm UTC](https://discourse.jupyter.org/t/spawn-single-user-notebook-on-kubernetes-cluster-using-kubespawner/15569 "2022-08-30T21:35:57Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Shraman](https://avatars.discourse-cdn.com/v4/letter/s/7feea3/32.png) [@Shraman](https://discourse.jupyter.org/u/Shraman)\
**Post date:** [August 30, 2022, 9:35pm UTC](https://discourse.jupyter.org/t/spawn-single-user-notebook-on-kubernetes-cluster-using-kubespawner/15569/1 "2022-08-30T21:35:57Z")

</div>

Hi Folks!  
I am trying to spawn single-user notebook on Kubernetes cluster using kubespawner . I am able to login using LDAP authenticator and I can see corresponding Kubernetes instance has been created in Kubernetes cluster. But service is not able to respond.  
I tried with increasing http\_timeout and start\_timeout but no luck yet and hoping to get some advice what might have gone wrong.

I have seen similar post and tried with all the suggestion, but nothing seems to be working so far.

Any help would be greatly appreciated. #NewtoCommunity

Environment:  
Jupyterhub : 2.2.2  
jupyterhub-kubespawner : 4.2.0

Config:  
import logging

c.JupyterHub.log\_level = logging.DEBUG  
c.KubeSpawner.debug = True  
c.SwarmSpawner.debug = True  
c.NotebookApp.allow\_root=True

############################################################################################################

# kubespawner.KubeSpawner

############################################################################################################  
import os  
import socket

c.JupyterHub.spawner\_class = ‘kubespawner.KubeSpawner’  
c.JupyterHub.ip = str(‘0.0.0.0’)  
c.JupyterHub.hub\_ip = str(‘127.0.0.1’)  
c.JupyterHub.cleanup\_servers = False  
c.KubeSpawner.start\_timeout = int(3600)  
c.Spawner.http\_timeout = int(60)  
c.JupyterHub.tornado\_settings = {‘slow\_spawn\_timeout’: 30}

if os.environ.get(“CI”):  
c.JupyterHub.hub\_connect\_ip = “127.0.0.1”  
else:  
s = socket.socket(socket.AF\_INET, socket.SOCK\_DGRAM)  
s.connect((“8.8.8.8”, 80))  
host\_ip = s.getsockname()[0]  
s.close()  
c.JupyterHub.hub\_connect\_ip = host\_ip

c.KubeSpawner.image = str(xxxxxxx:5000/xxxxxxx/bdp-jupyter-rc:7’)  
c.KubeSpawner.storage\_pvc\_ensure = False  
c.JupyterHub.allow\_named\_servers = True  
c.KubeSpawner.profile\_list = [  
{  
‘display\_name’: ‘Sandbox’,  
‘default’: True,  
‘kubespawner\_override’: {  
‘image’: xxxxxxxxx/bdp-jupyter-rc:7’,  
‘cpu\_limit’: 1,  
‘mem\_limit’: ‘512M’,  
}  
}  
]

Error :

 ![image](https://canada1.discourse-cdn.com/flex031/uploads/jupyter/original/2X/4/45aa7faf35ba3b1f08125fcbcf5e62415037fb67.png)

---

<div class="post-metadata">

**Author:** ![chrispward](https://avatars.discourse-cdn.com/v4/letter/c/f04885/32.png) [@chrispward](https://discourse.jupyter.org/u/chrispward)\
**Post date:** [September 7, 2022, 7:05pm UTC](https://discourse.jupyter.org/t/spawn-single-user-notebook-on-kubernetes-cluster-using-kubespawner/15569/2 "2022-09-07T19:05:39Z")

</div>

I think the property your looking for is the singleuser.startTimeout

I’ve attached details below on how I got mine working in k8.

I installed this to k8 using the following chart (below)  
Notes: I use keycloak as my authenticator… and using the lab teradata/jupyterlab-extensions  
I use a separate config map to load the connections… thats specific to the TD image.  
I’m also using Traefik as my ingress controller so I have the path https:///jhub so I dont need the loadbalancer. I create the ingressRoute seperatly as I enforce the TLS through it.  
I also change the default home directory through use of the cmds.

export NAMESPACE=jhub  
export RELASENAME=jupyterhub-dev  
export VERSION=1.2.0

kubectl config set-context --current --namespace=$NAMESPACE

helm upgrade --cleanup-on-fail --install $RELASENAME jupyterhub/jupyterhub --namespace $NAMESPACE --create-namespace --version=$VERSION --values jhub\_config.yaml

```auto
############Chart
# This file can update the JupyterHub Helm chart's default configuration values.
#
# For reference see the configuration reference and default values, but make
# sure to refer to the Helm chart version of interest to you!
#
# Introduction to YAML: https://www.youtube.com/watch?v=cdLNKUoMc6c
# Chart config reference: https://zero-to-jupyterhub.readthedocs.io/en/stable/resources/reference.html
# Chart default values: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/81aa3eb42a34cce00d134d88383f2212d041f6a8/jupyterhub/values.yaml
# Available chart versions: https://jupyterhub.github.io/helm-chart/
#
singleuser:
  cmd: ["jupyterhub-singleuser","--notebook-dir=/home"]
  startTimeout: 120
  defaultUrl: "/lab"
  image:
    name: teradata/jupyterlab-extensions
    tag: latest
  storage:
    homeMountPath: /home/jovyan/data
    capacity: 5Gi
    dynamic:
      storageClass: pmax-storage-policy
    extraVolumes:
      - name: connections
        configMap:
          name: connections
          items:
            - key: jhub_td_connections.yaml
              path: tdconfig.yaml
    extraVolumeMounts:
      - name: connections
        mountPath: /tmp/tdconfig
  extraEnv:
    accept_license: "Y"
    TD_CONFIG: /tmp/tdconfig/tdconfig.yaml
hub:
  db:
    pvc:
      storageClassName: pmax-storage-policy
  baseUrl: /jhub
  config:
    GenericOAuthenticator:
      client_id: <myclientId>
      client_secret: <Myclientsecret>
      oauth_callback_url: https://<mydomain>/jhub/hub/oauth_callback
      authorize_url: https://<mydomain>/keycloak/realms/<myrealm>/protocol/openid-connect/auth
      token_url: https://<mydomain>/keycloak/realms/<myrealm>/protocol/openid-connect/token
      userdata_url: https://<mydomain>/keycloak/realms/<myrealm>/protocol/openid-connect/userinfo
      login_service: keycloak
      username_key: preferred_username
      userdata_params:
        state: state
    JupyterHub:
      authenticator_class: generic-oauth

proxy:
  service:
    type: ClusterIP

```

---

<div class="post-metadata">

**Author:** ![Shraman](https://avatars.discourse-cdn.com/v4/letter/s/7feea3/32.png) [@Shraman](https://discourse.jupyter.org/u/Shraman)\
**Post date:** [September 7, 2022, 7:33pm UTC](https://discourse.jupyter.org/t/spawn-single-user-notebook-on-kubernetes-cluster-using-kubespawner/15569/3 "2022-09-07T19:33:41Z")

</div>

Thank you @chrispward,  
I am trying with helm chart now , as looks like that is preferred way.

While trying to set up custom service account for my singleuser , it is fetching default value which doenst have role define. Do i need to configure any other place then in Signleuser as below ?

**config.yaml**  
singleuser:  
serviceAccountName: “dev-sa”

**Error :**  
HTTP response body: b’{“kind”:“Status”,“apiVersion”:“v1”,“metadata”:{},“status”:“Failure”,“message”:“pods is forbidden: User \“system:serviceaccount:xxxxx-dev1: **default** \” cannot list resource \“pods\” in API group \”" in the namespace \“xxxxx-dev1\”",“reason”:“Forbidden”,“details”:{“kind”:“pods”},“code”:403}\n’

---

<div class="post-metadata">

**Author:** ![chrispward](https://avatars.discourse-cdn.com/v4/letter/c/f04885/32.png) [@chrispward](https://discourse.jupyter.org/u/chrispward)\
**Post date:** [September 8, 2022, 2:20pm UTC](https://discourse.jupyter.org/t/spawn-single-user-notebook-on-kubernetes-cluster-using-kubespawner/15569/4 "2022-09-08T14:20:57Z")

</div>

I didnt create/use a service account.  
Not sure if the charts auto create one. I think they do if you enable it.  
If not, you’d have to manually create one 1st.  
Looks like a priv issue (403) so the service account needs more privs.  
My advice… install it with the bare min 1st and then modify you values from there. k8 can be hard to trace so get it working, then make a small alteration and update etc.  
ie helm upgrade blah blah… etc.
