DOCS

Ports & previews

Expose a port inside a sandbox at a public preview URL. Three visibility tiers — public, private (qbox auth), and time-limited signed links.

Run a web app, API, or dev server inside a sandbox and reach it from the outside at a predictable URL:

http://sb_<id>-<port>.preview.<your-domain>

Traffic flows straight from the edge proxy to the app in the guest — the control plane only handles routing and access, never the bytes. HTTP and WebSockets are supported. Previews are served over HTTP for now (HTTPS previews are deferred).

Expose a port at spawn

import qbox

sb = qbox.Sandbox.create(
    "python-3.12",
    ports=[qbox.PortSpec(8080), qbox.PortSpec(3000, visibility="private")],
)

# the forwarding starts routing once the sandbox boots; read the URLs back:
for p in sb.ports.list():
    print(p.port, p.url)

Expose on a running sandbox

url = sb.ports.expose(8080)                      # public by default
sb.ports.expose(5432, visibility="private")      # requires qbox auth
link = sb.ports.expose(9000, visibility="signed_url", ttl=3600)  # shareable, expires

sb.ports.list()
sb.ports.unexpose(8080)

From the CLI:

# at create time — repeatable, PORT[:visibility]
qbox sandboxes create python-3.12 --port 8080 --port 3000:private

# on a running sandbox
qbox sandboxes ports expose sb_abc123 8080 --visibility public
qbox sandboxes ports list sb_abc123

From the dashboard: pick ports in the spawn dialog, or use the Ports tab on a running sandbox.

Visibility tiers

TierWho can reach it
publicanyone with the URL
privatesigned-in qbox users (optionally restricted to specific users)
signed_urlanyone holding a time-limited signed link

A signed link is minted when you expose with visibility="signed_url"; rotate it any time:

fresh = sb.ports.mint_signed_url(9000, ttl=600)

private previews are gated by your dashboard login: the proxy validates the qbox_session cookie against the control plane and, for a port with an allowed_users list, checks you’re on it. For that to work the cookie has to reach the preview subdomain, which means two settings:

  • QBOX_COOKIE_DOMAIN=<your-domain> — scopes the session cookie to your registrable domain so it’s shared with *.preview.<domain> (the installer sets this). Empty = host-only cookie, and a private preview just bounces you to the dashboard.
  • QBOX_COOKIE_SECURE=false — because previews are served over HTTP for now, a Secure cookie wouldn’t be sent to them. This sends the session cookie over HTTP too, so only enable private previews on a trusted network. For sharing over an untrusted network, prefer signed_url, which needs no cookie.

Reserved ports

Ports a sandbox service already owns are reserved and rejected: 22 when SSH is enabled, 8888 when the code interpreter is enabled. Privileged ports (<1024) require force=True (the dashboard and CLI set it for you when you ask for one).

Self-hosting setup

Previews need a real domain with wildcard DNS, because each preview lives on its own subdomain. On a self-hosted install:

  1. Point *.preview.<your-domain> (and <your-domain>) at your host’s public IP.
  2. Set QBOX_PROXY_DOMAIN=<your-domain>. The installer defaults this to your site domain and generates the signing + internal secrets automatically.

Caddy routes *.preview.<domain> to the proxy over plain HTTP — no certificates or DNS-provider tokens to set up. An IP-only / bare-port deployment can’t serve previews (there’s no domain to put them under). HTTPS previews are deferred; if you front the proxy with TLS yourself, set QBOX_PROXY_SCHEME=https. See Configuration for the full list.