# 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 ```ts check import { Sandbox } from "withruntime"; const sbx = await Sandbox.create({ timeoutSeconds: 3600 }); // keeps running after this script ends await 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 ``` ```python check from withruntime import Sandbox sbx = 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: ```text Blocked 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: ```ts check import { 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