Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions docs/src/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,36 @@ If this doesn't happen for some reason (it has been reported to not always work)

Most common notebook systems should work out of the box.

### IJulia
### IJulia (Classic Notebook)
![](ijulia.png)

### Jupyterlab
Classic Jupyter Notebook works out of the box using Jupyter's comm protocol.

### JupyterLab

![](jupyterlab.png)

JupyterLab requires the `jupyter-server-proxy` extension to be installed. This extension allows the browser to connect to Bonito's WebSocket server through JupyterLab's proxy.

**Installation:**
```bash
pip install jupyter-server-proxy
```

Then restart JupyterLab. Bonito will automatically detect and use the proxy.

!!! tip
Many JupyterLab distributions (JupyterHub, Binder, Google Colab) already include `jupyter-server-proxy`. You may not need to install it manually.

**Troubleshooting:**

If you're having connection issues in JupyterLab:

1. Verify `jupyter-server-proxy` is installed: `jupyter server extension list`
2. Look for `jupyter_server_proxy` in the output
3. If missing, install it and restart JupyterLab
4. For remote JupyterLab setups, you may need to set `BONITO_JUPYTER_REMOTE_HOST` environment variable

### Pluto
![](pluto.png)

Expand Down
50 changes: 36 additions & 14 deletions src/server-defaults.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,45 @@ const SERVER_CONFIGURATION = (
verbose=Ref(-1)
)

# Should only be called if IJulia is loaded!\
# Should only be called if IJulia is loaded!

function jupyter_running_servers()
function find_jupyter_executable()
# First try IJulia's configured jupyter
jupyter = IJulia().JUPYTER
if isfile(jupyter)
return jupyter
end
# Fallback: try to find jupyter in PATH (handles case where IJulia points to non-existent conda env)
jupyter_from_path = Sys.which("jupyter")
if !isnothing(jupyter_from_path)
return jupyter_from_path
end
# Give up - return original path, will fail with clear error message
return jupyter
end

function jupyter_running_servers()
jupyter = find_jupyter_executable()
# Man, whats up with jupyter??
# They switched between versions from stdout to stderr, and also don't produce valid json as output -.-
run_cmd(std, err) = run(pipeline(`$jupyter lab list --json`; stderr=err, stdout=std))
json = sprint(io -> run_cmd(io, IOBuffer()))
if isempty(json)
json = sprint(io -> run_cmd(IOBuffer(), io))
try
run_cmd(std, err) = run(pipeline(`$jupyter lab list --json`; stderr=err, stdout=std))
json = sprint(io -> run_cmd(io, IOBuffer()))
if isempty(json)
# give up -.-
return nothing
json = sprint(io -> run_cmd(IOBuffer(), io))
if isempty(json)
# give up -.-
return nothing
end
end
json = replace(json, "[JupyterServerListApp] " => "")
json = replace(json, r"[\r\n]+" => "\n")
configs = JSON.parse.(split(json, "\n"; keepempty=false))
return configs
catch e
@warn "Could not query jupyter servers" exception=(e, catch_backtrace())
return nothing
end
json = replace(json, "[JupyterServerListApp] " => "")
json = replace(json, r"[\r\n]+" => "\n")
configs = JSON.parse.(split(json, "\n"; keepempty=false))
return configs
end

function jupyterlab_proxy_url(port)
Expand All @@ -45,9 +65,11 @@ function jupyterlab_proxy_url(port)
hostname = config[1]["hostname"]
# TODO, this seems very fragile
if haskey(ENV, "BONITO_JUPYTER_REMOTE_HOST")
return string(ENV["BONITO_JUPYTER_REMOTE_HOST"], config[1]["base_url"], "proxy/", port)
# Note: jupyter-server-proxy mounts at /proxy/ on server root, not under base_url
return string(ENV["BONITO_JUPYTER_REMOTE_HOST"], "proxy/", port)
elseif hostname == "0.0.0.0" || hostname == "localhost"
return string("http://", IJulia().profile["ip"], ":", config[1]["port"], config[1]["base_url"], "proxy/", port)
# Note: jupyter-server-proxy mounts at /proxy/ on server root, not under base_url
return string("http://", IJulia().profile["ip"], ":", config[1]["port"], "/proxy/", port)
else
return ""
end
Expand Down