Mirror the public-link convenience Studio already has for its own UI, for JupyterLab. Off by default; opt in two ways: docker run -e UNSLOTH_JUPYTER_CLOUDFLARE=1 ... unsloth/unsloth docker exec <container> unsloth-jupyter-tunnel --force unsloth-jupyter-tunnel waits for JupyterLab, reuses a cached cloudflared (or fetches the static binary for the arch, no account needed), and starts a quick tunnel to the Jupyter port; the https://<name>.trycloudflare.com URL is printed to docker logs. supervisord runs it as the jupyter-cloudflare program, autostarted only when UNSLOTH_JUPYTER_CLOUDFLARE=1 (studio_launch.sh exports a 0 default so the autostart gate expands, matching the sshd pattern). JupyterLab still enforces its password, so the tunnel is not an open door. Verified: the helper fetches cloudflared and mints a working trycloudflare URL that reaches JupyterLab (HTTP 200) inside a running container.
60 lines
2.3 KiB
Bash
Executable file
60 lines
2.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Optional public Cloudflare quick-tunnel for JupyterLab, mirroring the tunnel
|
|
# Studio creates for its own UI. Off by default. Two ways to use it:
|
|
#
|
|
# * at run time: docker run -e UNSLOTH_JUPYTER_CLOUDFLARE=1 ... unsloth/unsloth
|
|
# -> the https://<name>.trycloudflare.com URL is printed in
|
|
# `docker logs` once JupyterLab is up.
|
|
# * on demand: docker exec <container> unsloth-jupyter-tunnel --force
|
|
#
|
|
# The tunnel gives a public https URL that works from anywhere with no account
|
|
# or open inbound port. JupyterLab still requires its password, so the notebook
|
|
# is not open to the world; treat the URL as sensitive all the same.
|
|
set -u
|
|
|
|
FORCE=0
|
|
[ "${1:-}" = "--force" ] && FORCE=1
|
|
if [ "$FORCE" != "1" ] && [ "${UNSLOTH_JUPYTER_CLOUDFLARE:-0}" != "1" ]; then
|
|
echo "[jupyter-tunnel] disabled (set UNSLOTH_JUPYTER_CLOUDFLARE=1, or run with --force)"
|
|
exit 0
|
|
fi
|
|
|
|
PORT="${JUPYTER_PORT:-8888}"
|
|
|
|
echo "[jupyter-tunnel] waiting for JupyterLab on port ${PORT} ..."
|
|
for _ in $(seq 1 90); do
|
|
if curl -fsS -o /dev/null "http://localhost:${PORT}/login" 2>/dev/null; then
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
# Reuse a cloudflared already on the host (Studio caches one for its own
|
|
# tunnel); otherwise fetch the static binary for this arch. No account needed.
|
|
CFD=""
|
|
for cand in \
|
|
"${UNSLOTH_STUDIO_HOME:-/opt/unsloth-studio}/bin/cloudflared" \
|
|
/usr/local/bin/cloudflared \
|
|
cloudflared; do
|
|
if command -v "$cand" >/dev/null 2>&1; then CFD="$(command -v "$cand")"; break; fi
|
|
[ -x "$cand" ] && { CFD="$cand"; break; }
|
|
done
|
|
if [ -z "$CFD" ]; then
|
|
case "$(uname -m)" in
|
|
x86_64|amd64) A=amd64;;
|
|
aarch64|arm64) A=arm64;;
|
|
*) A=amd64;;
|
|
esac
|
|
CFD=/usr/local/bin/cloudflared
|
|
echo "[jupyter-tunnel] downloading cloudflared (${A}) ..."
|
|
if ! curl -fsSL -o "$CFD" \
|
|
"https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${A}"; then
|
|
echo "[jupyter-tunnel] could not download cloudflared" >&2
|
|
exit 1
|
|
fi
|
|
chmod +x "$CFD"
|
|
fi
|
|
|
|
echo "[jupyter-tunnel] starting Cloudflare quick-tunnel to JupyterLab (port ${PORT})."
|
|
echo "[jupyter-tunnel] the https://<name>.trycloudflare.com URL appears below; log in with your Jupyter password."
|
|
exec "$CFD" tunnel --no-autoupdate --url "http://localhost:${PORT}"
|