How to preview a Vite dev server running in a cloud sandbox
Start vite --port 5173 --strictPort with spawn, allow .runtimehost.com as a host, and open the port's private HTTPS preview.
On Runtime the preview is private by default and hot reload works through it. The address needs a token, WebSockets pass through, and every file the agent writes shows up in the browser without a reload. The sandbox runs Node.js 24.21.0, which Vite 8 supports; Vite 8.3.1 and create-vite 9.2.1 were current on 25 September 2026.
Start Vite and share the port
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.create({ timeoutSeconds: 3600 }); // keeps running after this script endsawait sbx.exec( "npm create vite@9.2.1 app -- --template react-ts --no-interactive && cd app && npm install --no-fund --no-audit", { check: true, timeoutMs: 600_000 },);await sbx.spawn("npx vite --port 5173 --strictPort", { cwd: "/workspace/app", env: { __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: ".runtimehost.com" },});await sbx.exec("npx wait-on@9.1.0 tcp:127.0.0.1:5173", { check: true, timeoutMs: 120_000 });const preview = await sbx.previews.create(5173);console.log(preview.urlWithToken); // open once in your browserPythonfrom withruntime import Sandboxsbx = Sandbox.create(timeout_seconds=3600)sbx.exec("npm create vite@9.2.1 app -- --template react-ts --no-interactive && cd app && npm install --no-fund --no-audit", check=True, timeout_ms=600_000)sbx.spawn("npx vite --port 5173 --strictPort", cwd="/workspace/app", env={"__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS": ".runtimehost.com"})sbx.exec("npx wait-on@9.1.0 tcp:127.0.0.1:5173", check=True, timeout_ms=120_000)preview = sbx.previews.create(5173)print(preview["urlWithToken"])spawn keeps Vite running after the call returns. A server started with
exec, nohup included, ends when its command does. Vite listens on
localhost by default, and that is enough: a preview reaches the port inside
the sandbox.
Why Vite needs the allowed host
Vite answers only the hostnames in server.allowedHosts, plus localhost and
IP addresses, to block DNS rebinding attacks. A preview's address is a name
under runtimehost.com, the domain Runtime keeps for everything sandboxes
serve, so Vite refuses it until you add it.
| Way to allow it | Where |
|---|---|
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=.runtimehost.com |
The environment of spawn, as above; no file changes |
server: { allowedHosts: [".runtimehost.com"] } |
vite.config.ts, for a project you keep |
allowedHosts: true |
Not recommended: Vite's docs warn it opens the server to DNS rebinding |
A leading dot allows the domain and every name under it. vite preview
reads preview.allowedHosts, which defaults to server.allowedHosts.
What the browser shows without it, from a test of Vite 8.3.1 on 25 September 2026 with a preview-shaped host name:
textBlocked request. This host ("5173-abc.runtimehost.com") is not allowed.To allow this host, add "5173-abc.runtimehost.com" to `server.allowedHosts` in vite.config.js.That was a 403 from both vite and vite preview. With
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=.runtimehost.com set, both answered 200. If an agent reports this page, the variable is missing from the process
that runs Vite, often because it was started by hand instead of with the
env above.
Hot reload while an agent edits
Vite watches the project's files. Anything written into the sandbox, by
files.write, by git pull or by an agent's own editor, reaches the open
browser tab over Vite's WebSocket:
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.connect(process.env.SANDBOX_ID ?? "");await sbx.files.write( "/workspace/app/src/App.tsx", "export default function App() {\n return <h1>Edited by the agent</h1>;\n}\n",);--strictPort makes Vite exit when 5173 is taken, instead of moving to the
next free port and leaving the preview pointing at nothing. The process list,
sbx.processes.list(), shows whether Vite is still running.
Build and serve the production bundle
The dev server transforms files on request. To show what users would get, build
once and serve dist with vite preview:
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.connect(process.env.SANDBOX_ID ?? "");await sbx.exec("cd app && npx vite build", { check: true, timeoutMs: 300_000 });await sbx.spawn("npx vite preview --port 4173 --strictPort", { cwd: "/workspace/app", env: { __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: ".runtimehost.com" },});const shared = await sbx.previews.create(4173, { visibility: "public" });console.log(shared.url); // anyone with the address can open itawait sbx.files.download("/workspace/app/dist", "./dist");A public preview needs no token, which suits a demo link. previews.delete(port)
stops sharing, and previews.rotate(port) refuses every private token issued
so far.
Skip the install next time
A new sandbox for each project spends most of its first minute in
npm install. Build a custom image with the template already
installed, and hand its folder to the sandbox user:
Pythonfrom withruntime import Runtimeruntime = Runtime()runtime.images.build( name="vite-react", recipe={ "env": {"__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS": ".runtimehost.com"}, "commands": [ "npm create vite@9.2.1 app -- --template react-ts --no-interactive", "cd app && npm install --no-fund --no-audit", "chown -R 1000:1000 /workspace/app", ], },)sbx = runtime.sandboxes.create(image="vite-react")sbx.spawn("npx vite --port 5173 --strictPort", cwd="/workspace/app")print(sbx.previews.create(5173)["urlWithToken"])The recipe's env becomes the image's environment, so every sandbox from it
already allows the preview host.
Ports and addresses
| Port | What serves it | Default in Vite |
|---|---|---|
| 5173 | vite dev server |
server.port |
| 4173 | vite preview |
preview.port |
Any port can take a preview. The trial reaches ports 80 and 443 on the internet, which covers the npm registry.
Related
- Preview apps an AI agent builds
- Preview a Next.js app an agent built
- Run JavaScript and Node.js code in a sandbox
- Share a port
Sources
Checked 25 September 2026.
- Vite server options:
server.host(localhost),server.port(5173),server.allowedHosts, the leading-dot rule, the DNS rebinding warning and__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS - Vite preview options:
preview.port(4173) andpreview.allowedHosts - Vite getting started:
npm create vite@latest <name> -- --templateand--no-interactive - vite on npm: version 8.3.1, Node
^20.19.0 || >=22.12.0; create-vite 9.2.1 - A local test of Vite 8.3.1 and create-vite 9.2.1 on 25 September 2026: the
react-tstemplate scaffolded with--no-interactive, and the host check answered 403 without the variable and 200 with it, forviteandvite preview
Facts on this page were checked on 25 September 2026.