# How to run JavaScript and Node.js code in a sandbox Write the script into an isolated Linux microVM that already has Node.js, run `node` on it with a timeout, and read the output back. **On Runtime, Node.js 24.21.0, npm and Bun 1.4.0 are in every sandbox, so nothing installs before the first run.** Each sandbox is a Firecracker microVM with its own kernel, and a new one answered its first command 351 ms after the create request at the median on 24 September 2026 ([speed](/docs/speed)). Runtime bills the CPU the script uses, $0.025 per vCPU-hour, so a sandbox waiting on a slow API costs a fraction of one that computes ([pricing](/docs/pricing)). ## Run a script ```ts import { Sandbox } from "withruntime"; const script = ` const primes = []; for (let n = 2; primes.length < 10; n++) if (primes.every((p) => n % p)) primes.push(n); console.log(JSON.stringify(primes)); `; await using sbx = await Sandbox.create({ network: { internet: false }, timeoutSeconds: 300, onLeaseEnd: "stop", }); await sbx.files.write("/workspace/job.mjs", script); const run = await sbx.exec(["node", "job.mjs"], { timeoutMs: 20_000 }); console.log(run.exitCode, run.timedOut, run.stdout); ``` ```python from withruntime import Sandbox script = """ const primes = []; for (let n = 2; primes.length < 10; n++) if (primes.every((p) => n % p)) primes.push(n); console.log(JSON.stringify(primes)); """ with Sandbox.create(network={"internet": False}, timeout_seconds=300, on_lease_end="stop") as sbx: sbx.files.write("/workspace/job.mjs", script) run = sbx.exec(["node", "job.mjs"], timeout_ms=20_000) print(run.exit_code, run.timed_out, run.stdout) ``` An array runs `node` directly, with no shell, so nothing in the script or its arguments can inject a command. A script stuck in a loop comes back with `timedOut: true` and whatever it printed. ## What JavaScript has in the image | Tool | In the image | | -------------- | ------------------------------------ | | Node.js | 24.21.0: `node`, `npm`, `npx` | | Bun | 1.4.0: `bun` | | Native modules | `gcc`, `g++`, `make` and Python 3.12 | | Source | `git`, `curl`, `wget` | | OS | Ubuntu 24.04.5 LTS (noble), amd64 | Everything outbound goes through a proxy on the host. The image sets `HTTP_PROXY`, `HTTPS_PROXY` and `NODE_USE_ENV_PROXY=1`, so `npm`, `fetch` and most HTTP clients find it with no setup ([the network](/docs/sandbox-environment#the-network)). ## Install from npm, then close the network ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ network: { internet: true, allow: ["registry.npmjs.org"] }, timeoutSeconds: 600, onLeaseEnd: "stop", }); await sbx.exec("npm init -y && npm install --no-fund --no-audit lodash", { check: true, timeoutMs: 180_000, }); await sbx.network.set({ internet: false }); await sbx.files.write( "/workspace/job.cjs", "console.log(require('lodash').chunk([1, 2, 3, 4], 2))", ); console.log((await sbx.exec(["node", "job.cjs"], { timeoutMs: 20_000 })).stdout); ``` ```python check from withruntime import Sandbox with Sandbox.create( network={"internet": True, "allow": ["registry.npmjs.org"]}, timeout_seconds=600, on_lease_end="stop", ) as sbx: sbx.exec("npm init -y && npm install --no-fund --no-audit lodash", check=True, timeout_ms=180_000) sbx.network.set(internet=False) sbx.files.write("/workspace/job.cjs", "console.log(require('lodash').chunk([1, 2, 3, 4], 2))") print(sbx.exec(["node", "job.cjs"], timeout_ms=20_000).stdout) ``` `registry.npmjs.org` is npm's default registry ([npm](https://docs.npmjs.com/cli/v11/using-npm/registry)). Installing in the sandbox also keeps whatever a package does at install time off your server. ## A stateful JavaScript session The code interpreter keeps variables between cells, and an array of objects comes back as a table: ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ network: { internet: false } }); await sbx.interpreter.run("const rows = [{ city: 'Oslo', t: 4 }, { city: 'Rome', t: 19 }]", { language: "javascript", }); const cell = await sbx.interpreter.run("rows.map((r) => r.t).reduce((a, b) => a + b)", { language: "javascript", }); console.log(cell.results[0]?.data["text/plain"]); // 23 ``` ```python check from withruntime import Sandbox with Sandbox.create(network={"internet": False}) as sbx: sbx.interpreter.run("const rows = [{ city: 'Oslo', t: 4 }, { city: 'Rome', t: 19 }]", language="javascript") cell = sbx.interpreter.run("rows.map((r) => r.t).reduce((a, b) => a + b)", language="javascript") print(cell["results"][0]["data"]["text/plain"]) # 23 ``` The interpreter runs seven languages. Four are in the default image; the other three install from Ubuntu's archive the first time a sandbox uses them ([code interpreter](/docs/javascript#code-interpreter)): | Language | `language` | Ready | First use, 23 September 2026 | | ---------- | -------------- | ---------------------- | ---------------------------- | | Python | `"python"` | In the image (default) | No install | | JavaScript | `"javascript"` | In the image | No install | | TypeScript | `"typescript"` | In the image | No install | | Bash | `"bash"` | In the image | No install | | Java | `"java"` | Installed on first use | About 35 seconds, once | | Go | `"go"` | Installed on first use | About 35 seconds, once | | R | `"r"` | Installed on first use | About 90 seconds, once | ## Serve an app and look at it For an app rather than a script, start it with `spawn`, which keeps running after the call returns, and share the port as a private HTTPS [preview](/docs/javascript#share-a-port). An `exec` ends everything it started when its command ends. ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ timeoutSeconds: 1800 }); await sbx.files.upload("./app", "/workspace/app"); await sbx.exec("cd app && npm ci", { check: true, timeoutMs: 300_000 }); const server = await sbx.spawn("cd app && npm start"); console.log(server.id, server.info.state); ``` ## Bake your dependencies into an image ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.build({ name: "node-tools", recipe: { npm: ["typescript", "prettier"], apt: ["jq"] }, }); await using sbx = await runtime.sandboxes.create({ image: "node-tools" }); console.log((await sbx.exec(["npx", "prettier", "--version"])).stdout); ``` ```python check from withruntime import Runtime runtime = Runtime() runtime.images.build(name="node-tools", recipe={"npm": ["typescript", "prettier"], "apt": ["jq"]}) with runtime.sandboxes.create(image="node-tools") as sbx: print(sbx.exec(["npx", "prettier", "--version"]).stdout) ``` Building an image is free; a stored image is charged on its size ([custom images](/docs/images)). ## Related - [Run TypeScript code in a sandbox](/languages/typescript) - [Run Bun in a sandbox](/languages/bun) - [Run Deno code in a sandbox](/languages/deno) - [Preview apps an agent built](/use-cases/preview-agent-built-apps) - [Run Playwright in a sandbox](/integrations/playwright) New accounts get 50 free sandbox hours, no card: ```bash no-run npx withruntime sandbox run --trial -- node --version ``` ## Sources - npm, the public registry: https://docs.npmjs.com/cli/v11/using-npm/registry (read 25 September 2026) Facts on this page were checked on 25 September 2026.