Pause & Resume
How pause and resume works in Runra Sandbox — use cases, API examples, cost controls, and state preservation for AI agents
Pause and resume is one of Runra Sandbox's most powerful features. It lets you preserve the full state of a sandbox — filesystem, running processes, environment variables, and port mappings — without keeping the sandbox actively running (and billing).
How it works
When you pause a sandbox:
- All processes inside are checkpointed
- The full filesystem is snapshotted
- Environment variables and port mappings are saved as metadata
- The underlying VM resources are released
- You stop incurring active runtime costs
When you resume:
- A new VM is provisioned from the snapshot
- The filesystem is restored exactly as it was
- Processes that were checkpointed are restored
- Port mappings are re-established
- The sandbox returns to
runningstate
Lifecycle diagram
create → [running] → pause → [paused] → resume → [running]
↑ ↓
└────────── terminate ──────────────┘
API
Pause a running sandbox
const sandbox = await runra.sandboxes.create({
image: "node:22",
resources: { cpu: 2, memoryMb: 4096 },
});
await sandbox.exec("npm install && npm run build");
// Pause preserves the installed dependencies and build artifacts
await sandbox.pause();
console.log(sandbox.state); // "paused"
Resume a paused sandbox
const resumed = await runra.sandboxes.resume(sandbox.id);
console.log(resumed.state); // "running"
// All files from the previous session are still there
const result = await resumed.exec("ls node_modules");
console.log(result.stdout); // node_modules directory listing
Check sandbox state
const sandbox = await runra.sandboxes.get(sandbox.id);
console.log(sandbox.state); // "running" | "paused" | "terminated"
List paused sandboxes
const pausedSandboxes = await runra.sandboxes.list({
state: "paused",
limit: 50,
});
for (const sb of pausedSandboxes) {
console.log(`Sandbox ${sb.id}: paused at ${sb.pausedAt}`);
}
Use cases
Long-running coding agents
Agents working on complex refactors across multiple sessions can pause between sessions:
// Monday session
const sandbox = await runra.sandboxes.create({ image: "node:22" });
await sandbox.exec("git clone https://github.com/acme/monolith.git");
await sandbox.exec("cd monolith && npm install");
// Agent works for a few hours...
await sandbox.pause();
// Tuesday session — picks up exactly where it left off
const resumed = await runra.sandboxes.resume(sandbox.id);
await resumed.exec("cd monolith && git status");
// Working tree clean, all work from Monday preserved
User-specific workspaces
Give each user a persistent sandbox that remembers their state:
async function getUserSandbox(userId: string) {
// Check if user already has a paused sandbox
const existing = await runra.sandboxes.list({
metadata: { userId },
state: "paused",
limit: 1,
});
if (existing.length > 0) {
return runra.sandboxes.resume(existing[0].id);
}
return runra.sandboxes.create({
image: "python:3.12",
metadata: { userId },
resources: { cpu: 2, memoryMb: 4096 },
});
}
Multi-step research
Data scientists can run multi-stage experiments with state preserved between stages:
const sandbox = await runra.sandboxes.create({ image: "python:3.12" });
// Stage 1: Data loading and cleaning
await sandbox.exec("python load_data.py");
await sandbox.exec("python clean_data.py");
await sandbox.pause();
// Stage 2: Model training (resumed, data already cleaned)
const stage2 = await runra.sandboxes.resume(sandbox.id);
await stage2.exec("python train_model.py --epochs 50");
await stage2.pause();
// Stage 3: Evaluation
const stage3 = await runra.sandboxes.resume(sandbox.id);
const result = await stage3.exec("python evaluate.py");
Background automation
Scheduled jobs that need to maintain state across runs:
// Cron job runs every hour
async function hourlyCrawl() {
const sandbox = await getOrResumeSandbox("crawler-sandbox");
await sandbox.exec("python crawl.py --resume");
await sandbox.pause(); // Ready for next hour
}
Stateful tool-using agents
Agents that modify files and need those modifications to persist across tool calls:
const sandbox = await runra.sandboxes.create({ image: "node:22" });
async function agentToolLoop(instruction: string) {
let sandbox = await runra.sandboxes.resume(sessionStore.get("sandboxId"));
const tools = {
writeFile: async (path, content) => sandbox.files.write(path, content),
runCommand: async (cmd) => sandbox.exec(cmd),
readFile: async (path) => sandbox.files.read(path),
};
// Agent uses tools, all changes persist in sandbox
await runAgentLoop(instruction, tools);
await sandbox.pause(); // State preserved for next invocation
}
Cost-controlled warm environments
Keep development environments ready without paying for idle time:
// Create once, use when needed
async function getDevSandbox(projectId: string) {
const sandbox = await runra.sandboxes.resume(`dev-${projectId}`);
// Auto-pause after 30 minutes of inactivity
sandbox.on("idle", { thresholdMs: 1_800_000 }, async () => {
await sandbox.pause();
console.log(`Sandbox ${sandbox.id} auto-paused to save costs`);
});
return sandbox;
}
Billing model
| State | Billed? | Rate |
|---|---|---|
running | Yes | Per-second active runtime |
paused | Yes | Reduced storage-only rate |
terminated | No | N/A |
Paused sandboxes incur a minimal storage fee. The active runtime rate is significantly higher than the storage rate, so pausing idle sandboxes can yield substantial cost savings.
Lifecycle events
Track pause and resume via observability:
sandbox.on("paused", (event) => {
console.log(`Sandbox paused. Runtime: ${event.runtimeMs}ms`);
});
sandbox.on("resumed", (event) => {
console.log(`Sandbox resumed. Paused duration: ${event.pausedDurationMs}ms`);
});
Nest steps
- Sandboxes — full sandbox lifecycle
- Isolation Model — how state is isolated during pause
- Observability — track pause/resume events