Skip to main content

Ports and the Open button

How a port gets published​

A port inside your container is not reachable from the internet until the host publishes it. At launch, each declared container port is mapped to a free port on the host machine, taken from the range that host has available. The host port is what you connect to, and it is almost never the same number as the container port — the same reason your SSH port is not 22.

The container is told which host port each of its ports landed on, through one environment variable per published TCP port:

# inside the instance
env | grep SUPERHEAT_TCP_PORT_
# SUPERHEAT_TCP_PORT_22=40123
# SUPERHEAT_TCP_PORT_8080=41007

SUPERHEAT_TCP_PORT_<n> holds the published host port for container port <n>. This is informational: it is there so a process on the instance can advertise its own public address correctly. UDP ports get no such variable.

What each launch mode publishes​

Ports come from the template, plus whatever the launch mode requires:

Launch modePublished automaticallyThen
sshcontainer port 22plus every port declared on the template
jupytercontainer ports 22 and 8080plus every port declared on the template
argsnothing automaticonly the ports declared on the template
vmcontainer port 22plus every port declared on the template

A declared port may carry a label. A label of open, open_button or open-button marks that port as the Open button target. Every other label is descriptive only, and the port is treated as a plain application port.

The Open button​

When a template declares an open-button port, the instance's Connect tab shows an Open button above the SSH command, pointing at that port on the host. It is a shortcut for a web UI your image serves — an inference front end, a dashboard, a viewer — so you do not have to look the published port up.

If the host is behind NAT and cannot be reached directly, the Open button points at a relay address of the form https://<workload-id>-<container-port>.relay.superheat.cloud/ instead of the port on the host; the relay is switched on only once Superheat CN moves to its superheat.cloud domain (see Open JupyterLab).

The port number is also handed to the container as OPEN_BUTTON_PORT, in case the process needs to know which of its ports the console is advertising.

Every launch mode can have one, args included. An args-mode template runs the image's own entrypoint, but it still receives OPEN_BUTTON_PORT and still gets an Open button — without that, an image serving a web UI could never be opened from the console, which is most of what args mode is used for.

A jupyter-mode instance gets its own Open JupyterLab button in addition to this one. See Open JupyterLab.

The instance access token​

Each instance whose template needs one gets its own secret when you deploy it: the access token. It is the same value as the JupyterLab token. The instance's Connect tab shows it under Access token, masked, with Show access token and Copy access token buttons, and the API returns it as access_token on the instance to your organization. It is gone once the instance is destroyed.

Every curated template you can launch on Superheat CN that serves a web UI or an API on a published port requires the token on that port:

TemplatePortHow you present the token
ComfyUI, Stable Diffusion WebUI (A1111), Fooocus, Kohya_ss, Faster-Whisper (its speech-recognition API)the app's portAn access proxy sits in front of the app. The Open button signs you in; scripts send Authorization: Bearer <token>
Text Embeddings Inference80The server checks it as its API key: Authorization: Bearer <token> on every request
Open WebUI8080Sign in as admin@superheat.local with the token as the password. Sign-up is switched off, so nobody else can create an account

The token covers instances deployed after their template started requiring it. An older instance keeps its ports as they were — open to anyone who finds them — until you destroy it; deploy again to get the token.

Triton Inference Server requires the token too, but its image comes from NVIDIA NGC and shows as coming soon on Superheat CN; see Images on Superheat CN. Ollama, InvokeAI, LLaMA-Factory and vLLM are marked Coming soon for now. Their servers cannot require the token on every route — vLLM's API key leaves one inference route open — so they are off the shelf until they can. Instances already running from them are unaffected.

# An API port behind the token — Text Embeddings Inference here
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"inputs": "hello"}' \
http://203.0.113.14:41007/embed

How the Open button signs you in​

For the templates behind the access proxy, the Open link carries the token as ?token=. The first time you open it, the proxy swaps the token for a cookie in that browser and sends you on to the same address without it, so the token leaves the address bar straight away. Later visits in the same browser need no token. A request without the token or that cookie gets 401.

The cookie only works for the app's own pages and for opening the app directly. A request that a page of another origin makes your browser send — another port on the same machine included — is refused with 403, so another renter's app on the host cannot drive yours with your cookie.

A direct connection — the machine's IP address and a published port — is plain HTTP. The token in the Open link, the cookie and everything else on that connection travel unencrypted, so treat the Open link like a password: do not paste it anywhere you would not paste the token.

Browsers send a cookie to every port on the same address, not only to the one that set it. On a direct connection, opening an app that another renter runs on the same machine — any link to that machine's address with a different port — hands them your cookie, and with it your app, for as long as the instance lives. Open other people's links on that address in a different browser or a private window, or reach your own app over SSH forwarding instead.

Forwarding the port over SSH reaches the app without the proxy, because SSH has already authenticated you. Behind the proxy the app listens on 127.0.0.1, so forward to that address:

ssh -N -L 8188:127.0.0.1:8188 -p 40123 root@203.0.113.14

Your own templates​

A template of your own requires the token only on a port that declares how — the port's access setting in the template editor (Access proxy or App checks token), or auth over the API. A port without it is published exactly as before: open to anyone who finds the machine's address and port. Put something that authenticates in front of it, or reach it over SSH forwarding instead. See Ports.

Reaching a service you started yourself​

Ports are published when the instance launches. Starting a server inside a running instance does not publish anything, so a fresh python -m http.server 8000 is not reachable from outside no matter what the firewall on your laptop does.

You have two options.

Declare the port on a template and deploy again. This is the durable answer for anything you will run more than once. Add the port under the template's ports, give it a label, and redeploy. See Ports.

Forward it over SSH. This needs no redeploy and works immediately for a port you did not plan for:

ssh -N -L 8000:localhost:8000 -p 40123 root@203.0.113.14

Leave that running and open http://localhost:8000 in your browser. The traffic goes through the SSH connection you already have, so nothing new is exposed to the internet.

Whichever route you take, bind your service to 0.0.0.0 rather than 127.0.0.1 if you want the published port to work. A process listening only on loopback is reachable through SSH forwarding but not through a published host port. The one exception is an app behind the access proxy: there the app listens on loopback and the proxy owns the published port.

Protocols​

A declared port names a protocol: tcp, udp, http or https. The first two are what is actually reserved on the host. The last two are a hint about what the port serves, and they ride on TCP.