DocsPause, resume and snapshots

Pause, resume and snapshots

A paused sandbox keeps its memory and its disk, costs nothing while it sits there, and resumes where it stopped.

Pause a sandbox

pause() stops the sandbox and writes its state to a snapshot. Memory is kept by default, so running processes and open files survive.

import { Sandbox } from '@impello/sdk'

// Reads IMPELLO_API_KEY from the environment.
const sandbox = await Sandbox.create('base')
await sandbox.files.write('/home/user/state.json', '{"step": 1}')

const paused = await sandbox.pause()
console.log(paused) // true

The call returns true when it paused the sandbox and false when the sandbox was already paused, so pausing twice is safe. A sandbox that does not exist raises SandboxNotFoundError (SandboxNotFoundException in Python); see Errors.

Both SDKs also expose pause as a static call, so you can pause by ID without holding an instance: Sandbox.pause(sandboxId) in TypeScript and Sandbox.pause(sandbox_id) in Python.

Resume

Sandbox.connect(sandboxId) resumes a paused sandbox and hands back a client for it. The sandbox must be running or paused; anything else raises SandboxNotFoundError.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')
const sandboxId = sandbox.sandboxId
await sandbox.pause()

// Later, in another process.
const resumed = await Sandbox.connect(sandboxId, { timeoutMs: 600_000 })
console.log(await resumed.files.read('/home/user/state.json'))

await resumed.kill()

A resumed sandbox starts a fresh timeout: 5 minutes unless you pass one. timeoutMs is milliseconds in TypeScript, timeout is seconds in Python. For a running sandbox the timeout only moves if the new value is longer than the one already set. See Timeouts.

An instance you already hold has the same method: await sandbox.connect() in TypeScript, sandbox.connect() in Python. That is the shortest way back into a sandbox you paused a moment ago.

Disk-only pause

Pass keepMemory: false (keep_memory=False in Python) to drop the in-memory state and persist only the filesystem. Resuming such a sandbox cold-boots it from disk: the files are there, the running processes and open connections are not. A sandbox paused this way must be resumed explicitly with connect().

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')
await sandbox.pause({ keepMemory: false })
TypeScriptPythonDefaultWhat it does
keepMemorykeep_memorytrue / TrueKeep a full memory snapshot. false persists the filesystem only

Snapshots

A snapshot is a persistent image of a sandbox that you can start new sandboxes from. createSnapshot() pauses the sandbox while it takes the image; Sandbox.connect(sandboxId) brings it back. The call returns the snapshot's ID together with its full names.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')
await sandbox.files.write('/home/user/state.json', '{"step": 1}')

const snapshot = await sandbox.createSnapshot({ name: 'my-snapshot' })
console.log(snapshot.snapshotId, snapshot.names)

// Start a fresh sandbox from it.
const fromSnapshot = await Sandbox.create(snapshot.snapshotId)
console.log(await fromSnapshot.files.read('/home/user/state.json'))

await fromSnapshot.kill()
// Kill the source sandbox if you are done with it.
await sandbox.kill()

name is optional. Passing one that already exists adds a new build to that snapshot instead of creating a second one, so a nightly job can keep writing to the same name. names holds the namespaced, tag-qualified names of the snapshot, for example your-team/my-snapshot:v2. The part before the slash is your team namespace, not a project name.

Snapshots are persistent and survive the sandbox they came from: killing the source sandbox does not remove them, and they only go when you delete them.

Snapshots are not billed; see Limits and pricing. A deleted snapshot's data is removed within 30 days.

List snapshots

listSnapshots() returns a paginator. Called on an instance it lists only that sandbox's snapshots; called on Sandbox it lists all of them.

import { Sandbox } from '@impello/sdk'

const paginator = Sandbox.listSnapshots({ limit: 50 })

while (paginator.hasNext) {
  for (const snapshot of await paginator.nextItems()) {
    console.log(snapshot.snapshotId, snapshot.names)
  }
}

The static form takes sandboxId (sandbox_id); both forms take limit and nextToken (next_token). The Python SDK also takes name, which filters by snapshot name or ID and accepts a tag — "my-snapshot", "your-team/my-snapshot" or "my-snapshot:v1". The TypeScript SDK has no name filter.

Delete a snapshot

Pass the snapshotId (snapshot_id) that createSnapshot handed you. It is a namespaced, tag-qualified name, or the template ID with its tag (for example :latest) when you did not pass a name.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')
const snapshot = await sandbox.createSnapshot({ name: 'my-snapshot' })

const deleted = await Sandbox.deleteSnapshot(snapshot.snapshotId)
console.log(deleted) // false if there was no such snapshot

Fork a running sandbox

Forking is Python-only; the TypeScript SDK has no equivalent.

fork() checkpoints the sandbox in place: it is briefly paused, snapshotted with its full memory state, and resumed, keeping its ID and its expiry. It then starts count new sandboxes from that snapshot. The snapshot is captured once however many forks you ask for.

from impello import Sandbox

sandbox = Sandbox.create("base")
sandbox.commands.run("echo warm > /home/user/cache")

fork1, fork2 = sandbox.fork(count=2, timeout=600)

for fork in (fork1, fork2):
    if isinstance(fork, Exception):
        raise fork
    print(fork.sandbox_id, fork.commands.run("cat /home/user/cache").stdout)

count defaults to 1 and timeout is the forked sandboxes' own timeout in seconds, defaulting to 300. A count below 1 raises InvalidArgumentException.

There is a static form for forking by ID: Sandbox.fork(sandbox_id, count=2).

How long a paused sandbox lives

A sandbox that has sat paused for 14 days is deleted along with its disk. The clock restarts every time you pause, so a sandbox you resume and pause again gets a fresh 14 days. Snapshots are not affected by this; they persist until you delete them.

Paused sandboxes are not billed. They hold their disk and cost nothing, and billing starts again when they resume.

A paused sandbox does not count against your concurrency limit, and there is no limit on how many sandboxes you keep paused. See Limits and pricing.

To see what you have paused, filter the sandbox list by state.

import { Sandbox } from '@impello/sdk'

const paginator = Sandbox.list({ query: { state: ['paused'] } })

while (paginator.hasNext) {
  for (const s of await paginator.nextItems()) {
    console.log(s.sandboxId, s.state)
  }
}

AsyncSandbox has the same methods with every network call awaited, including fork() — except list() and list_snapshots(), which stay synchronous and hand back an AsyncSandboxPaginator / AsyncSnapshotPaginator whose next_items() you await.

Next: Metrics.