Skip to main content
Use Cases

Dev Servers

Run user-provided HTTP servers and request handlers inside Secure Exec and preview them from the host.

Previews, user-written request handlers, and generated web apps all need code that keeps running and answers requests. Secure Exec runs that server inside a VM and lets your backend talk to it, without opening a port on the host.

Start the server

import { createVm } from "secure-exec";

// The server is user-authored code, so it runs in a VM. Guest listeners stay on
// the VM's virtual network and do not need external network access.
const vm = await createVm();

// `spawn` returns as soon as the process starts, instead of waiting for the
// code to finish. The server prints a line once it is listening, so the host
// knows when it can send requests.
const ready = Promise.withResolvers<void>();
const decoder = new TextDecoder();
const server = await vm.javascript.spawn(
	`
	import { createServer } from "node:http";

	createServer((request, response) => {
		response.setHeader("content-type", "application/json");
		response.end(JSON.stringify({ path: request.url, pid: process.pid }));
	}).listen(3000, () => console.log("listening"));
	`,
	{
		onStdout: (chunk) => {
			if (decoder.decode(chunk).includes("listening")) ready.resolve();
		},
	},
);
console.log("server pid:", server.pid);
await ready.promise;

Proxy requests to it

Forward requests from your own route to the guest, and return what it answers.

// Send requests from the host to the port inside the VM. Nothing is exposed on
// the host's own network.
const response = await vm.network.httpRequest({ port: 3000, path: "/hello" });
console.log(new TextDecoder().decode(response.body)); // {"path":"/hello","pid":...}
  • No host port. The server listens on the VM’s virtual network. Only your process can reach it, through vm.network.httpRequest.
  • One VM per preview. Each preview gets its own filesystem and limits.
  • Bring a project. Write files with vm.filesystem, install dependencies with npm, then spawn the entry point.

Stop it

// Stop the server, then dispose the VM.
await vm.process.kill(server.pid);
const exit = await vm.process.wait(server.pid);
console.log("server exited:", exit);
await vm.dispose();

Read more in Long-Running Processes.