JupyterHub Deployment
This page describes how to deploy MetaKernel-based kernels on JupyterHub and how to configure a reverse proxy (Apache or nginx) in front of JupyterHub.
Installing a kernel for all users
On a shared JupyterHub server you typically want to install the kernel system-wide so that every user can select it:
# as root or with sudo
python -m my_kernel install --sys-prefix
If you run JupyterHub inside a conda environment, use --sys-prefix to
install into that environment's prefix. The --user flag is appropriate
for single-user testing but will not make the kernel visible to other
JupyterHub users.
Kernel discovery
JupyterHub spawns a separate single-user server for each user. The kernel
spec (installed under a shared prefix) is found automatically as long as
the JUPYTER_PATH environment variable includes the prefix's data
directory. No additional configuration is usually needed.
Reverse proxy configuration
JupyterHub must be reached through a single public URL (e.g.
https://hub.example.com/jupyter). Both Apache and nginx need special
settings to proxy WebSocket connections, which the Jupyter protocol relies
on for kernel communication.
Apache (mod_proxy / mod_proxy_wstunnel)
<VirtualHost *:443>
ServerName hub.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/hub.crt
SSLCertificateKeyFile /etc/ssl/private/hub.key
# Enable the required proxy modules:
# LoadModule proxy_module modules/mod_proxy.so
# LoadModule proxy_http_module modules/mod_proxy_http.so
# LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
ProxyPreserveHost On
# WebSocket upgrade (kernel channels, terminals)
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteRule /jupyter/(.*) ws://127.0.0.1:8000/jupyter/$1 [P,L]
# Standard HTTP
ProxyPass /jupyter/ http://127.0.0.1:8000/jupyter/
ProxyPassReverse /jupyter/ http://127.0.0.1:8000/jupyter/
# Required headers
RequestHeader set X-Scheme https
RequestHeader set X-Forwarded-Proto https
</VirtualHost>
Set the matching base URL in your jupyterhub_config.py:
c.JupyterHub.base_url = '/jupyter/'
nginx
server {
listen 443 ssl;
server_name hub.example.com;
ssl_certificate /etc/ssl/certs/hub.crt;
ssl_certificate_key /etc/ssl/private/hub.key;
location /jupyter/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Increase timeouts for long-running kernels
proxy_read_timeout 86400;
}
}
Jigsaw magic under JupyterHub
The %jigsaw magic embeds a Blockly visual editor in an IFrame. In a
JupyterHub environment the IFrame is served from the user's file space
(/user/{name}/files/workspace.html), which has a different origin
from the notebook page. Modern browsers block direct window.parent
property access across origins, causing a SecurityError.
MetaKernel handles this automatically by:
- Replacing
window.parent.*property access withpostMessage— the iframe sends structured messages to the parent page instead of accessing its DOM directly. - Embedding saved workspace XML in the HTML — instead of fetching the
.xmlfile via XHR (which is also blocked from a cross-origin or sandboxed iframe), the XML is serialised into a JavaScript variable inside the generated HTML file at magic-run time. - WebSocket kernel execution fallback — when the classic
IPython.notebookor JupyterLab JS APIs are not reachable, the helper falls back to discovering the kernel via the REST/api/sessionsendpoint and connects directly over WebSocket. The base URL is read from thejupyter-config-dataelement injected by the server, so the correct JupyterHub-prefixed path is used automatically.
No special configuration is required — these fixes are applied every time
%jigsaw is executed.
Known limitations
- The
%jigsawmagic writes the generated.htmland.xmlworkspace files to the current working directory of the kernel. On JupyterHub this is typically the user's home directory, so the files are accessible through the file browser. - Very restrictive Content Security Policies set by the proxy (e.g.
frame-src 'self') may still prevent the IFrame from loading. If you control the proxy, allowframe-srcfor the JupyterHub origin.
Troubleshooting
- Kernel does not appear in the launcher
- Check that the kernel spec is installed under a prefix that is on
JUPYTER_PATH. Runjupyter kernelspec listas the spawned user to verify. %jigsawshows a blank iframe / SecurityError in the browser console- Make sure you are running a recent version of MetaKernel (≥ 1.0).
Earlier versions wrote HTML that accessed
window.parentdirectly, which is blocked across origins. - WebSocket connections fail through the proxy
- Ensure
mod_proxy_wstunnel(Apache) orproxy_http_version 1.1+Upgrade/Connectionheaders (nginx) are configured. WebSocket upgrades require explicit proxy support. - 404 on
/jupyter/after settingbase_url - The trailing slash in
c.JupyterHub.base_url = '/jupyter/'is required. The proxyProxyPass/locationblock must use the same path prefix.