Skip to content
Runtime

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.5 and create-vite 9.3.1 were current on 25 October 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.3.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:localhost:5173", { check: true, timeoutMs: 120_000 });const preview = await sbx.previews.create(5173);console.log(preview.urlWithToken); // open once in your browser
Pythonfrom withruntime import Sandboxsbx = Sandbox.create(timeout_seconds=3600)sbx.exec("npm create vite@9.3.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:localhost: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 accepts requests only for the hostnames in server.allowedHosts, plus localhost and IP addresses. This blocks 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.5 on 25 October 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 returned 200. If an agent reports this page, the variable is missing from the process that runs Vite. That often happens when the agent started Vite 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. It does not move to the next free port, which would leave 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);console.log(shared.urlWithToken); // the link carries its tokenawait sbx.files.download("/workspace/app/dist", "./dist");

On a paid sandbox, { visibility: "public" } gives an address with no token, which suits a demo link. Previews on a sandbox without credit stay private. 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. Then 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.3.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 made 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. Without credit, a sandbox reaches ports 80 and 443 on the internet, which covers the npm registry.

Sources

Checked 25 October 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) and preview.allowedHosts
  • Vite getting started: npm create vite@latest <name> -- --template and --no-interactive
  • vite on npm: version 8.3.5, Node ^20.19.0 || >=22.12.0; create-vite 9.3.1
  • A local test of Vite 8.3.5 and create-vite 9.3.1 on 25 October 2026: the react-ts template scaffolded with --no-interactive, and the host check returned 403 without the variable and 200 with it, for vite and vite preview

Facts on this page were checked on 25 October 2026.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card