# How to run a background process in a sandbox Call `spawn()` with the command; it starts a server, watcher or REPL, returns at once, and keeps running after you disconnect. **On Runtime a background process outlives the connection that started it, and a paused sandbox keeps it running across the pause.** Reconnect from another process and get it back by id, with its output from the start. A sandbox whose server sits waiting costs $0.03125 an hour at 2 vCPU and 4 GiB, and $0.08 an hour with both CPUs busy ([pricing](/docs/pricing), checked 25 September 2026). ## Start a server and a REPL ```ts import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create(); const server = await sbx.spawn("python3 -m http.server 8000", { cwd: "/workspace" }); console.log(server.id, server.info.state); const repl = await sbx.spawn(["python3", "-i", "-q"], { stdin: "pipe" }); await repl.write("print(21 * 2)\n"); await repl.write("exit()\n", { eof: true }); console.log((await repl.wait()).stdout); for (const p of await sbx.processes.list()) console.log(p.id, p.state, p.command); await server.kill("SIGTERM"); ``` ```python from withruntime import Sandbox with Sandbox.create() as sbx: server = sbx.spawn("python3 -m http.server 8000", cwd="/workspace") print(server.id, server.info["state"]) repl = sbx.spawn(["python3", "-i", "-q"], stdin="pipe") repl.write("print(21 * 2)\n") repl.write("exit()\n", eof=True) print(repl.wait().stdout) for process in sbx.processes(): print(process["id"], process["state"], process["command"]) server.kill("SIGTERM") ``` A string runs under `bash -c`; a list runs the program with no shell. `stdin: "pipe"` keeps the process's input open so `write()` can feed it, and `eof` closes it. ## From the command line `spawn` prints the process id, `ps` lists processes, and `logs` prints what one has written so far and returns, even while it runs: ```bash id=$(runtime sandbox create) pid=$(runtime sandbox spawn "${id}" -- python3 -m http.server 8000) runtime sandbox ps "${id}" runtime sandbox logs "${id}" "${pid}" runtime sandbox kill "${id}" "${pid}" runtime sandbox stop "${id}" ``` ```bash no-run runtime sandbox logs "${id}" "${pid}" -f # follow until the process exits ``` An agent over MCP calls `runtime_sandbox_exec` with `"background": true`, then `runtime_sandbox_process` with `"action": "read"` to follow its output ([MCP](/docs/mcp)). ## Get a process back later A process does not belong to the script that started it. Save its id, and any later process reconnects and reads from wherever it left off: ```ts check import { Sandbox } from "withruntime"; const sbx = await Sandbox.connect(process.env.SANDBOX_ID!); const job = await sbx.processes.get(process.env.PROCESS_ID!); for await (const event of job.output({ cursor: 0 })) { if (event.type === "stdout") process.stdout.write(event.data); if (event.type === "exit") console.log("exit", event.exitCode); } ``` ```python check import os from withruntime import Sandbox sbx = Sandbox.connect(os.environ["SANDBOX_ID"]) job = sbx.process(os.environ["PROCESS_ID"]) for event in job.output(cursor=0): if event["type"] == "stdout": print(event["data"], end="") ``` `output()` yields every event from the start, or from a `cursor`, until the process exits. ## What a process gets | Setting or limit | Value | | ---------------- | ---------------------------------------------------------------------- | | Returns | At once, with the process id and its state | | Input | `stdin: "pipe"` (Python `stdin="pipe"`) for `write()`; `eof` closes it | | Signals | `kill("SIGTERM")` or any signal name; `POST …/processes/{id}:signal` | | Output kept | The latest 1 MiB of each process's output, in the sandbox | | Across a pause | Running processes are kept with memory and carry on after the wake | | Lifetime | Until it exits, is killed, or the sandbox stops | | Terminal | Add `pty: { cols, rows }` for a program that needs one | | Cost | No charge per process; the sandbox's measured CPU and reserved memory | ## Mistakes to avoid - **Starting a server with `exec`.** A process started by `exec` ends with its command, and `exec` waits for it. A server, a file watcher or a queue worker goes in `spawn`. - **Printing more than 1 MiB and reading it later.** A reader that was away while more than that came out gets a `truncated` event saying how many bytes are gone, and `wait()` sets both truncation flags. Send a chatty process's output to a file and read the file. - **Forgetting the lease.** A sandbox runs until its lease ends, then pauses by default, and its processes pause with it. For a server that must answer all day, see [keep a sandbox running](/how-to/keep-a-sandbox-running). - **Opening a port with nothing listening.** Check `processes.list()` shows the server as running before you share its port with a preview or `runtime sandbox port-forward`. ## Related - [Stream command output](/how-to/stream-command-output) for commands that end. - [Open an interactive terminal](/how-to/open-an-interactive-terminal) for a shell you type into. - [Preview agent-built apps](/use-cases/preview-agent-built-apps): a spawned dev server, shared at an HTTPS address. - [Background processes](/docs/javascript#background-processes) in the SDK reference. Facts on this page were checked on 25 September 2026.