Has anyone experience with hub-managed services in JupyterHub?
I’m trying to make a deployment of JupyterHub (5.2.1) that includes the hub-managed service jhub-apps (2025.2.1) on Azure Kubernetes (1.30.9). Everything works well (authentication, singleuser servers etc…), but not the jhub-apps service. Here is a link to the their repo (GitHub - nebari-dev/jhub-apps: Application creator and general launcher for JupyterHub).
Deployment is installed using helm with the JupyterHub helm chart version 4.1.0. ‘jhub-apps’ is installed in a custom hub image, which is derived from jupyterhub/k8s-hub:4.1.0 (the default image for that helm chart). I’m turning on ‘jhub-apps’ using the ‘extraConfig’ of the helm deployment yaml file (see below). The deployment works fine, if I’m skipping the ‘install_jhub_apps’ and the reconfiguration of the ‘theme_template_parts’ part at the end
The hub pod logs (see below) show that everything was started correctly. However, when the home screen /hub/home is supposed to come up, an error 503 seems to sent out. The proxy pod logs show this more clearly with 503 messages: ERRCONNREFUSED. (see below, too)
What am I doing wrong? I already tried things like turning off network policies that might block important ports (like port 10202), and even tried with a earlier versions of JupyterHub (4.0.2).
Any help with troubleshooting is greatly appreciated!
These are the logs of the proxy pod:
It looks like if the proxy pod does not route to the japps service correctly. I think it should be routing to http:// hub:10202, not to https:// 127.0.0.1:10202 ? But How to change that?
21:34:49.951 [ConfigProxy] info: Adding route / -> http://hub:8081
21:34:50.018 [ConfigProxy] info: Proxying https://*:8443 to http://hub:8081
21:34:50.018 [ConfigProxy] info: Proxying https://*:8443 to http://hub:8081
21:34:50.018 [ConfigProxy] info: Proxy API at http://*:8001/api/routes
21:34:50.020 [ConfigProxy] info: Added HTTP to HTTPS redirection from 8000 to 443
21:34:50.021 [ConfigProxy] info: Route added / -> http://hub:8081
21:35:04.995 [ConfigProxy] info: 200 GET /api/routes
21:35:08.113 [ConfigProxy] info: 200 GET /api/routes
21:35:08.118 [ConfigProxy] info: Adding route / -> http://hub:8081
21:35:08.118 [ConfigProxy] info: Route added / -> http://hub:8081
21:35:08.119 [ConfigProxy] info: 201 POST /api/routes/
21:35:08.120 [ConfigProxy] info: Adding route /services/japps -> http://127.0.0.1:10202
21:35:08.121 [ConfigProxy] info: Route added /services/japps -> http://127.0.0.1:10202
21:35:08.122 [ConfigProxy] info: 201 POST /api/routes/services/japps
21:36:05.891 [ConfigProxy] error: 503 GET /services/japps/static/css/index.css connect ECONNREFUSED 127.0.0.1:10202
21:36:05.894 [ConfigProxy] error: 503 GET /services/japps/static/js/index.js connect ECONNREFUSED 127.0.0.1:10202
21:36:08.127 [ConfigProxy] info: 200 GET /api/routes
21:36:35.366 [ConfigProxy] error: 503 GET /services/japps/ connect ECONNREFUSED 127.0.0.1:10202
Deployment yaml file (for helm deployment):
scheduling:
corePods:
nodeAffinity:
matchNodePurpose: prefer
userScheduler:
enabled: false
podPriority:
enabled: true
userPlaceholder:
enabled: false
cull:
enabled: false
singleuser:
networkPolicy:
egressAllowRules:
privateIPs: true
defaultUrl: "/lab"
image:
name: quay.io/jupyter/base-notebook
tag: 2025-03-14
storage:
capacity: 5Gi
proxy:
service:
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
service.beta.kubernetes.io/azure-load-balancer-ipv4: "<ip-address>"
chp:
networkPolicy:
enabled: false
traefik:
networkPolicy:
enabled: false
https:
enabled: true
hosts:
- <dns-address>
type: secret
secret:
name: the-cert
hub:
image:
name: >registry-name>.azurecr.io/<hub-image-name>
tag: <hub-tag>
networkPolicy:
enabled: false
config:
Authenticator:
admin_users:
- the admin
AzureAdOAuthenticator:
client_id: "<client-id>"
client_secret: "<client-secret>"
oauth_callback_url: "https://<dns-address>/hub/oauth_callback"
tenant_id: "<tenant-id>"
JupyterHub:
authenticator_class: azuread
extraConfig:
my_config: |
c.Authenticator.allow_all = True
c.KubeSpawner.default_url = "/lab"
from jhub_apps.configuration import install_jhub_apps
from jhub_apps import theme_template_paths
from kubespawner import KubeSpawner
c.JupyterHub.bind_url = "https://<dns-address>"
c.JupyterHub.default_url = "/hub/home"
c.JAppsConfig.allowed_frameworks = ["jupyterlab"]
c.JAppsConfig.jupyterhub_config_path = "/usr/local/etc/jupyterhub/jupyterhub_config.py"
c.JupyterHub.template_paths = theme_template_paths
c = install_jhub_apps(c, spawner_to_subclass=KubeSpawner)
These are the logs of the hub pod:
Loading /usr/local/etc/jupyterhub/secret/values.yaml
No config at /usr/local/etc/jupyterhub/existing-secret/values.yaml
Loading extra config: my_config
[I 2025-03-15 21:35:04.620 JupyterHub app:3346] Running JupyterHub version 5.2.1
[I 2025-03-15 21:35:04.621 JupyterHub app:3376] Using Authenticator: oauthenticator.azuread.AzureAdOAuthenticator-17.3.0
[I 2025-03-15 21:35:04.621 JupyterHub app:3376] Using Spawner: jhub_apps.spawner.spawner_creation.JHubSpawner
[I 2025-03-15 21:35:04.621 JupyterHub app:3376] Using Proxy: jupyterhub.proxy.ConfigurableHTTPProxy-5.2.1
/usr/local/lib/python3.12/site-packages/jupyter_events/schema.py:68: JupyterEventsVersionWarning: The `version` property of an event schema must be a string. It has been type coerced, but in a future version of this library, it will fail to validate. Please update schema: https://schema.jupyter.org/jupyterhub/events/server-action
validate_schema(_schema)
[I 2025-03-15 21:35:04.825 JupyterHub app:2881] Creating service japps with oauth_client_id=service-japps
[I 2025-03-15 21:35:04.829 JupyterHub provider:663] Updating oauth client service-japps
[I 2025-03-15 21:35:04.920 JupyterHub app:2919] Creating service japps-initialize-startup-apps without oauth.
[I 2025-03-15 21:35:04.978 JupyterHub app:3416] Initialized 0 spawners in 0.011 seconds
[I 2025-03-15 21:35:04.985 JupyterHub metrics:373] Found 0 active users in the last ActiveUserPeriods.twenty_four_hours
[I 2025-03-15 21:35:04.986 JupyterHub metrics:373] Found 0 active users in the last ActiveUserPeriods.seven_days
[I 2025-03-15 21:35:04.987 JupyterHub metrics:373] Found 0 active users in the last ActiveUserPeriods.thirty_days
[I 2025-03-15 21:35:04.988 JupyterHub app:3703] Not starting proxy
[I 2025-03-15 21:35:04.995 JupyterHub app:3739] Hub API listening on http://:8081/hub/
[I 2025-03-15 21:35:04.995 JupyterHub app:3741] Private Hub API connect url http://hub:8081/hub/
[I 2025-03-15 21:35:04.996 JupyterHub app:3615] Starting managed service japps at http://127.0.0.1:10202
[I 2025-03-15 21:35:04.996 JupyterHub service:423] Starting service 'japps': ['python', '-m', 'uvicorn', 'jhub_apps.service.app:app', '--port=10202', '--host=0.0.0.0', '--workers=2']
[I 2025-03-15 21:35:04.997 JupyterHub service:136] Spawning python -m uvicorn jhub_apps.service.app:app --port=10202 --host=0.0.0.0 --workers=2
INFO: Uvicorn running on http://0.0.0.0:10202 (Press CTRL+C to quit)
INFO: Started parent process [12]
INFO: Started server process [14]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [15]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: 127.0.0.1:34084 - "GET /services/japps HTTP/1.1" 307 Temporary Redirect
[I 2025-03-15 21:35:08.108 JupyterHub app:3615] Starting managed service japps-initialize-startup-apps
[I 2025-03-15 21:35:08.108 JupyterHub service:423] Starting service 'japps-initialize-startup-apps': ['/bin/sh', '-c', 'python -m jhub_apps.tasks.commands.initialize_startup_apps && tail -f /dev/null']
[I 2025-03-15 21:35:08.110 JupyterHub service:136] Spawning /bin/sh -c 'python -m jhub_apps.tasks.commands.initialize_startup_apps && tail -f /dev/null'
[I 2025-03-15 21:35:08.113 JupyterHub proxy:477] Adding route for Hub: / => http://hub:8081
[W 2025-03-15 21:35:08.114 JupyterHub proxy:445] Adding missing route for japps (Server(url=http://127.0.0.1:10202/services/japps/, bind_url=http://127.0.0.1:10202/services/japps/))
[I 2025-03-15 21:35:08.114 JupyterHub proxy:312] Adding service japps to proxy /services/japps/ => http://127.0.0.1:10202
[I 2025-03-15 21:35:08.122 JupyterHub app:3772] JupyterHub is now running, internal Hub API at http://hub:8081/hub/
2025-03-15 21:35:10,693 INFO jhub_apps.service.utils: 45: event='Getting JHub config from file: /usr/local/etc/jupyterhub/jupyterhub_config.py' view=None peer=None
2025-03-15 21:35:11,351 INFO jhub_apps.service.utils: 50: event="JHub Apps config: {'allowed_frameworks': ['jupyterlab'], 'jupyterhub_config_path': '/usr/local/etc/jupyterhub/jupyterhub_config.py', 'app_icon': 'https://jupyter.org/assets/homepage/main-logo.svg', 'app_title': 'JHub Apps Launcher', 'apps_auth_type': 'oauth', 'blocked_frameworks': None, 'conda_envs': [], 'hub_host': '127.0.0.1', 'python_exec': 'python', 'service_workers': 2, 'startup_apps': [], 'config':{...}, 'JAppsConfig': {...}}, 'log': <_FixedFindCallerLogger traitlets (INFO)>, 'parent': None}" view=None peer=None
2025-03-15 21:35:11,351 INFO __main__: 51: event='Finished instantiating startup apps' view=None peer=None
Loading /usr/local/etc/jupyterhub/secret/values.yaml
No config at /usr/local/etc/jupyterhub/existing-secret/values.yaml
Loading extra config: my_config
[I 2025-03-15 21:36:00.967 JupyterHub log:192] 302 GET / -> /hub/ (@::ffff:10.100.200.36) 1.18ms
[W 2025-03-15 21:36:01.011 JupyterHub base:482] Invalid or expired cookie token
[I 2025-03-15 21:36:01.012 JupyterHub log:192] 302 GET /hub/ -> /hub/home (@::ffff:10.100.200.36) 1.74ms
[I 2025-03-15 21:36:01.060 JupyterHub log:192] 302 GET /hub/home -> /hub/login?next=%2Fhub%2Fhome (@::ffff:10.100.200.36) 1.24ms
[I 2025-03-15 21:36:01.099 JupyterHub _xsrf_utils:125] Setting new xsrf cookie for b'70ce983bbab043bb9e73de95cef3af96:Vv6f8Mq9y2qwph-Gea583S4T972AIajjH9jRZu7cMDw=' {'path': '/hub/', 'max_age': 3600}
[I 2025-03-15 21:36:01.162 JupyterHub log:192] 200 GET /hub/login?next=%2Fhub%2Fhome (@::ffff:10.100.200.36) 64.61ms
[I 2025-03-15 21:36:03.832 JupyterHub oauth2:126] OAuth redirect: https://<dns-address>/hub/oauth_callback
[I 2025-03-15 21:36:03.833 JupyterHub log:192] 302 GET /hub/oauth_login?next=%2Fhub%2Fhome -> https://login.microsoftonline.com/<....>
[I 2025-03-15 21:36:05.747 JupyterHub _xsrf_utils:125] Setting new xsrf cookie for b'70ce983bbab043bb9e73de95cef3af96:7cd0de7dd8e046cab0aeeb2cc5d0c76b' {'path': '/hub/'}
[I 2025-03-15 21:36:05.747 JupyterHub base:973] User logged in: the admin
[I 2025-03-15 21:36:05.748 JupyterHub log:192] 302 GET /hub/oauth_callback?code=[secret]&state=[secret]&session_state=[secret] -> /hub/home (user name@::ffff:10.100.200.36) 256.11ms
[I 2025-03-15 21:36:05.826 JupyterHub log:192] 200 GET /hub/home (the admin@::ffff:10.100.200.36) 39.54ms
[I 2025-03-15 21:36:05.894 JupyterHub _xsrf_utils:125] Setting new xsrf cookie for b'None:d_vj8HY38q0MrtNc84UHiW7g3ty5C9wR20bsl4_urTI=' {'path': '/hub/', 'max_age': 3600}
[I 2025-03-15 21:36:05.902 JupyterHub log:192] 200 GET /hub/error/503?url=%2Fservices%2Fjapps%2Fstatic%2Fcss%2Findex.css%3Fv%3D20250315213504 (@10.100.200.58) 8.66ms
[I 2025-03-15 21:36:05.903 JupyterHub _xsrf_utils:125] Setting new xsrf cookie for b'None:d_vj8HY38q0MrtNc84UHiW7g3ty5C9wR20bsl4_urTI=' {'path': '/hub/', 'max_age': 3600}
[I 2025-03-15 21:36:05.905 JupyterHub log:192] 200 GET /hub/error/503?url=%2Fservices%2Fjapps%2Fstatic%2Fjs%2Findex.js%3Fv%3D20250315213504 (@10.100.200.58) 2.28ms
INFO: 127.0.0.1:50250 - "GET /services/japps HTTP/1.1" 307 Temporary Redirect
[I 2025-03-15 21:36:35.367 JupyterHub _xsrf_utils:125] Setting new xsrf cookie for b'None:d_vj8HY38q0MrtNc84UHiW7g3ty5C9wR20bsl4_urTI=' {'path': '/hub/', 'max_age': 3600}
[I 2025-03-15 21:36:35.369 JupyterHub log:192] 200 GET /hub/error/503?url=%2Fservices%2Fjapps%2F (@10.100.200.58) 2.52ms