Lifecycle & Hibernation
A sandbox moves through five states. The transitions between them are the only operations that cost money: an idle hibernated sandbox costs nothing on the runner side.
create() exec() ┌──────────────► running ◄────────┐ │ │ │ │ hibernate() │ │ ▼ │ │ hibernated │ │ │ │ │ resume() │ │ ▼ │ │ running ──────────┘ │ └─ destroy() ─► destroyedCreate
Boots a Firecracker microVM in 27 ms on bare metal (cold start, no warm pool). The VM has its own kernel, 1 GiB RAM, and a 3 GiB scratch disk that grows on write.
from isorun import Sandbox
sb = Sandbox("python:3.12")sb.create()print(sb.id) # run<16-hex>print(sb.info.create_ms) # 27curl -X POST https://api.isorun.ai/v1/runs \ -H "Authorization: Bearer $ISORUN_API_KEY" \ -d '{"image": "python:3.12"}'The recommended pattern is the context manager — it auto-destroys on exit even if your code throws:
with Sandbox("python:3.12") as sb: sb.exec("python3 -c 'print(42)'")# sandbox is destroyed hereHibernate
Pause the VM, snapshot it to local disk, and free its FC process / TAP device / cgroup. The runID stays valid; the user perceives a paused workspace they can come back to. The runner only spends resources when the sandbox is actively running, so a hibernated sandbox is essentially free.
sb = Sandbox("python:3.12")sb.create()sb.exec("pip install numpy pandas") # 30s of setup work
# User closes their laptop. We hibernate the workspace.sb.hibernate() # ~470 ms
# Hours later, the user comes back…sb.resume() # ~21 ms — faster than a cold create
# State is identical to what it was at hibernate time:# the pip-installed packages are still there, the cwd is preserved,# and any background processes the user started are still running.sb.exec("python3 -c 'import numpy; print(numpy.__version__)'")# Hibernatecurl -X POST https://api.isorun.ai/v1/runs/$RID/hibernate \ -H "Authorization: Bearer $ISORUN_API_KEY"# → {"status": "hibernated", "id": "run..."}
# Resumecurl -X POST https://api.isorun.ai/v1/runs/$RID/resume \ -H "Authorization: Bearer $ISORUN_API_KEY"# → {"status": "running", "id": "run..."}What survives a hibernate/resume cycle:
- Filesystem state. Anything you wrote to the scratch disk is intact.
- Memory state. Every page the VM had loaded is preserved — including in-memory caches, JIT-compiled bytecode, and database working sets.
- Process state. Background processes the user started before
hibernation are still running at the same PIDs after resume. A
long-running
node server.js, ajupyter notebookkernel, an SSH agent — they all survive. - Open file descriptors and listen sockets. The VM’s network stack is re-attached to a fresh TAP device with a new guest IP, so any ESTABLISHED TCP connections from before hibernation will be reset. Listen sockets and the kernel’s TCP stack survive cleanly.
Resume
Restores a hibernated sandbox in 21 ms — faster than a fresh create, because the resume path skips the golden-snapshot pre-warm overhead. The user perceives a workspace that’s “always there” without paying for it during idle periods.
The same runID is reused across hibernate/resume cycles, so client code that holds a reference to a sandbox doesn’t need to track a new identity.
Destroy
Frees the sandbox permanently, including any hibernation snapshot on disk. Returns measured resource usage — these are the numbers you’re billed on.
stats = sb.destroy()print(stats.cpu_ms) # 1247print(stats.mem_peak_bytes) # 84_672_512print(stats.disk_used_bytes) # 12_288print(stats.uptime_ms) # 8_421curl -X DELETE https://api.isorun.ai/v1/runs/$RID \ -H "Authorization: Bearer $ISORUN_API_KEY"Auto-destroy timeout
Every sandbox has an idle timeout (default 5 minutes, configurable
per-create with sandbox_timeout). When the timer fires, the sandbox
is destroyed. Set sandbox_timeout=-1 to disable the timer entirely
for long-running workloads.
Hibernation is the right answer when you want the workspace to persist but the VM resources released. The user-facing UX is “always on”; the runner-facing cost is “only when you’re actively running.”
What each transition costs
| Transition | Time on bare metal | What’s happening |
|---|---|---|
create() | 27 ms | FC restore from golden snapshot, vsock init, TAP attach |
exec() (separate calls) | ~7 ms | vsock dial, agent fork, command run |
exec() inside a shell() | ~0.1 ms | no fork — pipes into the long-lived bash |
fork() (per child after the first) | ~16 ms | hardlink mem.snap, reflink scratch, FC restore |
hibernate() | 469 ms | FC snapshot/create + reflink scratch + cleanup |
resume() | 21 ms | FC restore + vsock init |
destroy() | 9 ms | kill FC process, free TAP/cgroup, cleanup runDir |
Per-second billing means an idle sandbox costs about $0.000011/sec running, and zero while hibernated.