On Runtime, a file watch survives a pause and loses nothing. Reading a
watch never wakes a paused sandbox. Delivery stops. After the wake, the watch
continues from the same place. It does what E2B's watchDir does. It also adds
include and exclude globs, batching and overflow notices. A paused sandbox costs
$0.08 per decimal GB of saved state a month, and no compute
(pricing, checked 26 October 2026).
Watch a directory
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();const watch = await sbx.files.watch("/workspace", (event) => console.log(event.type, event.path), { recursive: true, exclude: ["node_modules", ".git/**"], onNotice: (notice) => console.warn("rescan:", notice), onExit: (reason) => console.log("watch ended:", reason),});await sbx.exec("echo hi > /workspace/note.txt");await watch.stop();Pythonfrom withruntime import Sandboxwith Sandbox.create() as sbx: watch = sbx.files.watch("/workspace", recursive=True, exclude=["node_modules", ".git/**"]) sbx.exec("echo hi > /workspace/note.txt") for event in watch.get_new_events(wait_ms=2000): print(event["type"], event["path"]) print(watch.notices) watch.stop()In Python, get_new_events(wait_ms=...) returns the events that have arrived.
watch.events() iterates until the watch ends. A rename event carries the old
path as well.
From a terminal, watch prints each change until Ctrl-C:
Terminalruntime sandboxes watch "${id}" /workspace --recursive --exclude node_modules --exclude '.git/**'runtime sandboxes watch "${id}" /workspace/src --events create,write --include '**/*.py'An agent over MCP uses runtime_sandboxes_files_watch to start a watch, read
its events after a cursor, list watches and stop one (MCP).
After a pause
When its sandbox pauses, a watch stops delivering. It resumes from where it stopped:
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.create({ idlePauseSeconds: 600 });const watch = await sbx.files.watch("/workspace/src", (e) => console.log(e.type, e.path), { recursive: true, onExit: async (reason) => { if (reason === "paused") { await sbx.wake(); watch.resume(); // nothing lost } },});In Python, events() stops with exit_reason == "paused". Calling it again
after the wake continues from the same place.
Options and limits
| Option or limit | Value |
|---|---|
| Events | create, write, remove, rename (with the old path), chmod |
recursive |
Watch subdirectories; a new directory is scanned as it appears |
include, exclude |
Globs; an excluded directory is not watched at all |
events |
Only these kinds, such as create,write on the CLI |
| Batching | Repeated writes to one file in a batch come as one event with a count |
| Watches per sandbox | At most four |
timeoutMs |
One hour by default, then the watch ends; 0 runs until stopped |
webhook |
Runtime sends the changes to your webhooks as sandbox.files.changed |
| Flood cap | 5,000 events a second; the rest reported as an overflow notice, never silently |
| Over HTTP | POST /v1/sandboxes/{id}/files/watches; events after a cursor, waitMs to 8000 |
| Cost | No charge per watch; the sandbox's measured CPU and reserved memory |
Changes pushed to your server
A watch started with webhook: true needs nothing reading it. Runtime reads
it and POSTs what changed to your account's
webhooks, signed. It retries for about three days
when your server does not answer. Nothing of yours runs in the sandbox. So a
sync service that ran its own inotify watcher and an HTTP client in there can
drop both. Each event lists up to 100 changes gathered over at
least 2 seconds, and an account keeps up to 16 such
watches.
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();await sbx.files.watches.webhook("/workspace/app", { recursive: true, exclude: ["node_modules"] });Reading it never wakes a paused sandbox. While a sandbox that pauses itself does nothing, the watch is not read at all. So it never keeps the sandbox running. The first change after that arrives within seconds of the sandbox doing work again.
Mistakes to avoid
- Watching
node_modulesor.git. They change in bursts of thousands of files. Exclude them. An excluded directory is not watched at all, so its churn never reaches the watch. - Assuming a quiet watch means no changes. After a flood past 5,000 events
a second, the watch sends an
overflownotice throughonNotice(orwatch.noticesin Python). The notice says how many were dropped. Rescan the directory when you see one. - A fifth watch. A sandbox runs at most four watches. To cover many folders,
watch a common parent with
recursiveand filter withinclude. Do not start one watch per folder. - Treating
onExit("paused")as the end. It means the sandbox paused. Callwatch.resume()once it wakes, and the stream continues. - Leaving it to time out silently. A watch ends after
timeoutMs, an hour by default. HandleonExit, and start a new watch if you still need it.
Where this helps
- A coding agent edits files while your UI shows each change live, as in Claude Code in a sandbox.
- A dev server's source folder is watched to rebuild or reload a preview.
- A grader collects each file a student's program writes (grade student code).
The SDK reference is at watch files.
Facts on this page were checked on 26 October 2026.