Docs / concepts / pause-resume

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:

  1. All processes inside are checkpointed
  2. The full filesystem is snapshotted
  3. Environment variables and port mappings are saved as metadata
  4. The underlying VM resources are released
  5. You stop incurring active runtime costs

When you resume:

  1. A new VM is provisioned from the snapshot
  2. The filesystem is restored exactly as it was
  3. Processes that were checkpointed are restored
  4. Port mappings are re-established
  5. The sandbox returns to running state

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

StateBilled?Rate
runningYesPer-second active runtime
pausedYesReduced storage-only rate
terminatedNoN/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