Skip to content

Open a port as an app#

Anything you run in a runtime that serves HTTP — TensorBoard, a Streamlit or Gradio demo, Spark's UI — opens in your browser at an address of its own, r-<id>-<port>.<domain>. Nothing is opened on the machine: the machine's own outbound connection carries it, and it connects to the port inside the container.

Before you begin#

  • A running environment, or a notebook's runtime (Ready or Starting). A stopped one answers 409 RUNTIME_NOT_RUNNING: start it first.
  • To be its owner, one of the people it is shared with, or an admin of the workspace — the same people may open its apps.
  • The server must listen on a port of the container, on any of its addresses: 127.0.0.1 is enough. Not port 2222 (SSH), nor the runtime's own server: 8888 in a notebook's runtime (Jupyter: open the notebook), 8822 in an IDE environment (the IDE: Open IDE).
  • For the CLI tabs, astra signed in (Install the CLI); for the API tabs, ASTRAEUS_TOKEN, CONSOLE and API as in Create and change a runtime.

1. Start the server in the runtime#

In a terminal of the runtime (astra ssh dev, an IDE terminal, or a notebook cell with !), start the server in the background. For example, in an environment of the PyTorch preset made with --pip tensorboard==2.21.0 --pip streamlit==1.64.0 --pip gradio==6.29.0:

$ cd /content/projects && mkdir -p runs
$ python -c "
from torch.utils.tensorboard import SummaryWriter
w = SummaryWriter('runs/demo')
for step in range(100):
    w.add_scalar('loss', 1.0 / (step + 1), step)
w.close()"
$ nohup ~/.local/bin/tensorboard --logdir runs --port 6006 > tensorboard.log 2>&1 &

An app receives no cookies, so Streamlit's upload protection (its XSRF check) must be off:

$ mkdir -p /content/projects && cd /content/projects
$ cat > app.py <<'EOF'
import streamlit as st
st.title("Hello from the GPU machine")
n = st.slider("Points", 10, 1000, 100)
st.line_chart([i * i for i in range(n)])
EOF
$ nohup ~/.local/bin/streamlit run app.py --server.port 8501 --server.headless true \
    --server.enableXsrfProtection false > streamlit.log 2>&1 &

$ mkdir -p /content/projects && cd /content/projects
$ cat > demo.py <<'EOF'
import gradio as gr
gr.Interface(fn=lambda name: f"Hello, {name}!", inputs="text", outputs="text").launch(server_port=7860)
EOF
$ nohup python demo.py > gradio.log 2>&1 &

In a runtime of the Spark preset, a Spark session serves its UI on 4040 while it is open. In a notebook cell:

from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("demo").getOrCreate()
spark.range(10_000_000).selectExpr("sum(id)").show()

2. Open it#

  1. Open the environment's page (Hesperus → Environments → the environment; for a notebook's runtime with SSH, Connect in Hesperus → Runtimes).
  2. Under Apps, press Open port…. The dialog suggests TensorBoard · 6006 for the deep learning presets, Spark UI · 4040 for Spark, and Streamlit · 8501 and Gradio · 7860 for any.
  3. Enter the Port and, optionally, a Name. Remember it on this environment (checked) lists it under Apps with an Open button.
  4. Press Open. It opens in a new tab at its own address.

Forget takes an app off the list; the app keeps running.

$ astra env port dev 6006 --as TensorBoard
Opened https://r-4f1c9a0b2d7e83a65c10-6006.<runtime domain>
$ astra env port dev 8501 --print
https://r-4f1c9a0b2d7e83a65c10-8501.<runtime domain>/__astralyx/enter?ticket=ast_rtt_…
(works once, within a minute: it signs the browser that opens it in to https://r-4f1c9a0b2d7e83a65c10-8501.<runtime domain> only)

astra env port <runtime> <port> takes any runtime you may use, a notebook's included. --as <name> also remembers it under that name (listed on the environment's page); --print prints the link instead of opening a browser — it works once, within a minute.

Name apps on the runtime (the list replaces the previous one; at most 16, names at most 40 characters):

$ curl -fsS -X PUT "$API/notebook-runtimes/dev/apps" -H "Authorization: Bearer $ASTRAEUS_TOKEN" \
    -H 'content-type: application/json' \
    -d '{"apps": [{"name": "TensorBoard", "port": 6006}, {"name": "Streamlit", "port": 8501}]}' | jq '.spec.apps'

And ask for a link into one (any port, named or not):

$ curl -fsS -X POST "$CONSOLE/clusters/<cluster>/runtime-links" -H "Authorization: Bearer $ASTRAEUS_TOKEN" \
    -H 'content-type: application/json' -d '{"runtime": "dev", "port": 6006}' | jq '{url, origin, expires_at}'

The url works once, within a minute; the browser that follows it is signed in to that address for 8 hours.

Who reaches an app#

The same people as the runtime: its owner, the people it is shared with and the workspace's admins, each signed in with their own account. Taking someone off the runtime's people refuses their next request. Each address is signed in separately, for 8 hours; open it again from the console after that.

What an app may not rely on:

  • Cookies. No cookie reaches an app, and none it sets reaches the browser. Apps that need their own (Streamlit's upload protection) run without it.
  • Being framed or called from another site. An app's pages are not shown inside other sites, and cross-origin requests to it are refused.
  • Raw TCP. Only HTTP and WebSockets. For anything else, forward the port over SSH (Use ssh, scp and rsync).

Requests to an app are activity: an app in use keeps the runtime from stopping as idle.