Skip to content

Commit 7562ee0

Browse files
authored
Add alternative interfaces and SSH into hub guides (#285)
1 parent 6402972 commit 7562ee0

File tree

5 files changed

+169
-0
lines changed

5 files changed

+169
-0
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Alternative user interfaces
2+
3+
2i2c hubs can run other web applications (for example VS Code, RStudio, or a desktop) inside the same user server. These rely on [`jupyter-server-proxy`](https://jupyter-server-proxy.readthedocs.io/) packages shipped in the user image (see [](./customize.md) to add the proxy packages to your own image).
4+
5+
## How it works
6+
7+
- `jupyter-server-proxy` runs in the user container and forwards web requests to a local process started by a launcher tile or URL such as `/vscode`, `/rstudio`, or `/desktop`.
8+
- A small proxy package (for example `jupyter-vscode-proxy`, `jupyter-rsession-proxy`, or `jupyter-remote-desktop-proxy`) registers the launcher entry and start command for each application.
9+
10+
:::{note}
11+
12+
All interfaces share the same CPU, memory, and storage limits as the Jupyter server.
13+
14+
:::
15+
16+
Once configured, users see new launcher tiles in JupyterLab. Each tile opens the selected interface in a new authenticated browser tab. Below we show an example for a [Linux Desktop and QGIS](#interfaces:desktop).
17+
18+
```{figure} images/jupyterlab-alternative-launcher.png
19+
:alt: JupyterLab launcher showing tiles for VS Code, RStudio, Terminal, and Desktop
20+
:width: 90%
21+
22+
Example launcher showing additional interfaces alongside the default Notebook tile.
23+
```
24+
25+
## Prerequisites
26+
27+
Your [user image](customize.md) needs:
28+
29+
1. The application binary (for example [`code-server`](https://github.com/coder/code-server), [`rstudio-server`](https://posit.co/download/rstudio-server/), or a desktop stack).
30+
2. The matching proxy package so Jupyter knows how to start and route to it.
31+
3. `jupyter-server-proxy` installed in the environment.
32+
33+
Many community-maintained images (for example Pangeo or Rocker stacks) already include these pieces. If you build your own image, add the proxy package with your package manager (for example `pip install jupyter-vscode-proxy`) so the launcher tile is registered. See [](customize.md) for guidance on building custom images.
34+
35+
## Configure a profile that includes the interface
36+
37+
Expose an interface by pointing a `profileList` entry at an image that contains the app and proxy. Optionally, set `default_url` to the proxy route so users land in the right interface.
38+
39+
### VS Code (code-server)
40+
41+
`jupyter-vscode-proxy` lets you proxy `code-server` (VS Code in the browser), allowing users to develop in VS Code without leaving the hub.
42+
43+
Here is an example from the [Strudel](https://strudel.science/) hub that launches VS Code via `code-server`:
44+
45+
- [JupyterHub configuration](https://github.com/2i2c-org/infrastructure/blob/a042ecc16ed9d7111eece2ff19261446e69cc0e2/config/clusters/strudel/common.values.yaml#L57-L66)
46+
- [User image configuration repository](https://github.com/strudel-science/strudel-infra)
47+
48+
Pangeo images ship `code-server` with `jupyter-vscode-proxy`. This image is an example of one that already contains the required packages. You can substitute your own image if it includes `code-server` and `jupyter-vscode-proxy`.
49+
50+
```
51+
jupyterhub:
52+
singleuser:
53+
profileList:
54+
- display_name: "VS Code"
55+
slug: "vscode"
56+
kubespawner_override:
57+
image: "pangeo/pangeo-notebook:2024.04.16" # includes code-server + jupyter-vscode-proxy
58+
default_url: /vscode # send users straight to VS Code
59+
```
60+
61+
Pin to a specific image tag and adjust resource limits if your community needs more memory for VS Code.
62+
63+
(interfaces:desktop)=
64+
### Linux Desktop (VNC)
65+
66+
`jupyter-remote-desktop-proxy` lets you proxy desktop interfaces as well, allowing users to run GUI applications in the browser.
67+
68+
Here are examples for the desktop proxy (any image with `jupyter-remote-desktop-proxy` works):
69+
70+
* NASA VEDA (QGIS desktop profile):
71+
- [Hub configuration](https://github.com/2i2c-org/infrastructure/blob/a042ecc16ed9d7111eece2ff19261446e69cc0e2/config/clusters/nasa-veda/common.values.yaml#L166-L173)
72+
- [User environment image configuration repository](https://github.com/2i2c-org/nasa-qgis-image)
73+
74+
Images with `jupyter-remote-desktop-proxy` expose a lightweight [XFCE](https://www.xfce.org/) desktop for GUI tools such as [QGIS](https://qgis.org/) or [MATLAB](https://www.mathworks.com/products/matlab.html). Use any image that includes `jupyter-remote-desktop-proxy`. The one below is for illustration.
75+
76+
```
77+
jupyterhub:
78+
singleuser:
79+
profileList:
80+
- display_name: "Linux Desktop"
81+
slug: "desktop"
82+
kubespawner_override:
83+
image: "quay.io/2i2c/nasa-qgis-image:0d0765090250" # includes jupyter-remote-desktop-proxy
84+
default_url: /desktop # open the desktop interface
85+
```
86+
87+
```{figure} images/alternative-ui-arcgis.png
88+
:alt: ArcGIS Desktop running in a browser via the remote desktop interface
89+
:width: 90%
90+
91+
Example remote desktop session running [ArcGIS Pro](https://www.esri.com/en-us/arcgis/products/arcgis-pro/overview) through the desktop profile.
92+
```
93+
94+
:::{seealso}
95+
96+
* [Jupyter Server Proxy Documentation](https://jupyter-server-proxy.readthedocs.io/) - Authoritative technical guidance.
97+
* [Jupyter Remote Desktop Proxy](https://github.com/jupyterhub/jupyter-remote-desktop-proxy) - The tool used to provide the desktop experience.
98+
* [RStudio on Binder/JupyterHub via Rocker](https://rocker-project.org/images/versioned/binder.html) - Documentation on Jupyter-compatible R images.
99+
* [Not Just for Notebooks: JupyterHub in 2025](https://www.youtube.com/watch?v=vsbHMvvsFw8) - A recent talk by Yuvi Panda on these interfaces (includes others not shown here, like OpenRefine).
100+
* [Launching alternative UIs on JupyterHub](https://www.youtube.com/watch?v=MvZ-UUpqYMw) - A short walkthrough of VS Code, RStudio, and desktop launchers from [Konstantin Taletskiy](https://taletskiy.com/).
101+
:::
410 KB
Loading
143 KB
Loading

admin/environment/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ These sections describe this environment and how you can customize your environm
1111
:maxdepth: 2
1212
defaults
1313
interfaces
14+
alternative-interfaces
15+
ssh-access
1416
customize
1517
mount-repositories
1618
```

admin/environment/ssh-access.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Remote SSH access
2+
3+
You can let users connect to their hub session with SSH for terminal work, VS Code Remote, or file transfer with `scp`/`rsync`. This runs an SSH server inside the same user container and exposes it over HTTPS.
4+
5+
## How it works
6+
7+
- [`jupyter-sshd-proxy`](https://github.com/yuvipanda/jupyter-sshd-proxy) starts `sshd` inside the user server and publishes it through [`jupyter-server-proxy`](https://jupyter-server-proxy.readthedocs.io/) at `/sshd/` over WebSockets.
8+
- Authentication and authorization come from JupyterHub - no extra inbound ports or firewall changes are needed.
9+
- The SSH session uses the same CPU, memory, and storage limits as the Jupyter server.
10+
11+
## User image requirements
12+
13+
Install the following in the user image (see [](customize.md)):
14+
15+
- `openssh-server`
16+
- `jupyter-sshd-proxy`
17+
- `jupyter-server-proxy`
18+
19+
Example `values.yaml` profile:
20+
21+
```
22+
jupyterhub:
23+
singleuser:
24+
profileList:
25+
- display_name: "SSH enabled"
26+
slug: "ssh"
27+
kubespawner_override:
28+
image: "quay.io/yuvipanda/pangeo-jupyter-sshd-proxy:latest" # includes jupyter-sshd-proxy
29+
```
30+
31+
You can also add these packages to your own image instead of using the example image above.
32+
33+
## Local setup (users)
34+
35+
1. Install a WebSocket helper for your SSH client (recommended: [`websocat`](https://github.com/vi/websocat/releases)).
36+
2. Create a JupyterHub API token from `/hub/token` on your hub.
37+
3. Add an SSH config entry in `~/.ssh/config` (replace placeholders):
38+
39+
```
40+
Host myhub
41+
HostName <your-hub-hostname> # e.g., myhub.pilot.2i2c.cloud
42+
User jovyan
43+
ProxyCommand websocat --binary -H="Authorization: token <API_TOKEN>" asyncstdio:wss://%h/user/<JUPYTERHUB_USERNAME>/sshd/
44+
```
45+
46+
Then connect with `ssh myhub` or point VS Code Remote - SSH at the `myhub` host. The ProxyCommand is reused by `scp` and `rsync`.
47+
48+
:::{admonition} Keep these tokens private!
49+
Keep the API token private and rotate it from `/hub/token` if it is exposed.
50+
:::
51+
52+
## Community examples
53+
54+
* Openscapes (https://openscapes.org/) uses this [workflow for VS Code Remote and large data transfers](https://openscapes.cloud/ssh-into-hub.html)
55+
* Configuration pull request: https://github.com/Openscapes/openscapes.cloud/pull/67
56+
57+
:::{seealso}
58+
59+
* [jupyter-sshd-proxy repository](https://github.com/yuvipanda/jupyter-sshd-proxy) - setup and troubleshooting
60+
* [VS Code Remote - SSH](https://code.visualstudio.com/docs/remote/ssh) - connect the VS Code client to the SSH host
61+
* [Not Just for Notebooks: JupyterHub in 2025](https://www.youtube.com/watch?v=vsbHMvvsFw8) - talk covering SSH access and other interfaces for JupyterHub
62+
:::
63+
64+
## Acknowledgements
65+
66+
- This guide was originally [written by Andy Teucher for OpenScapes](https://github.com/Openscapes/openscapes.cloud/pull/67) and its content was modified for these docs.

0 commit comments

Comments
 (0)