# 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.

<CodeTabs>
<Tab label="TypeScript">

```ts
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
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox

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

paused = sandbox.pause()
print(paused)  # True
```

</Tab>
</CodeTabs>

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](/docs/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`.

<CodeTabs>
<Tab label="TypeScript">

```ts
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()
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox

sandbox = Sandbox.create("base")
sandbox_id = sandbox.sandbox_id
sandbox.pause()

# Later, in another process.
resumed = Sandbox.connect(sandbox_id, timeout=600)
print(resumed.files.read("/home/user/state.json"))

resumed.kill()
```

</Tab>
</CodeTabs>

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](/docs/sandbox/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()`.

<CodeTabs>
<Tab label="TypeScript">

```ts
import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')
await sandbox.pause({ keepMemory: false })
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox

sandbox = Sandbox.create("base")
sandbox.pause(keep_memory=False)
```

</Tab>
</CodeTabs>

<table>
  <thead>
    <tr><th>TypeScript</th><th>Python</th><th>Default</th><th>What it does</th></tr>
  </thead>
  <tbody>
    <tr><td>`keepMemory`</td><td>`keep_memory`</td><td>`true` / `True`</td><td>Keep a full memory snapshot. `false` persists the filesystem only</td></tr>
  </tbody>
</table>

## 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.

<CodeTabs>
<Tab label="TypeScript">

```ts
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()
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox

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

snapshot = sandbox.create_snapshot(name="my-snapshot")
print(snapshot.snapshot_id, snapshot.names)

# Start a fresh sandbox from it.
from_snapshot = Sandbox.create(snapshot.snapshot_id)
print(from_snapshot.files.read("/home/user/state.json"))

from_snapshot.kill()
# Kill the source sandbox if you are done with it.
sandbox.kill()
```

</Tab>
</CodeTabs>

`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](/docs/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.

<CodeTabs>
<Tab label="TypeScript">

```ts
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)
  }
}
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox

paginator = Sandbox.list_snapshots(limit=50)

while paginator.has_next:
    for snapshot in paginator.next_items():
        print(snapshot.snapshot_id, snapshot.names)
```

</Tab>
</CodeTabs>

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.

<CodeTabs>
<Tab label="TypeScript">

```ts
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
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox

sandbox = Sandbox.create("base")
snapshot = sandbox.create_snapshot(name="my-snapshot")

deleted = Sandbox.delete_snapshot(snapshot.snapshot_id)
print(deleted)  # False if there was no such snapshot
```

</Tab>
</CodeTabs>

## 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.

```python
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`.

<Callout kind="warning">
Each fork succeeds or fails on its own. The returned list holds one entry per
requested fork: a sandbox, or an exception explaining why that one did not
start. Check every entry before you use it.
</Callout>

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](/docs/limits-and-pricing).

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

<CodeTabs>
<Tab label="TypeScript">

```ts
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)
  }
}
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox, SandboxQuery, SandboxState

paginator = Sandbox.list(query=SandboxQuery(state=[SandboxState.PAUSED]))

while paginator.has_next:
    for s in paginator.next_items():
        print(s.sandbox_id, s.state)
```

</Tab>
</CodeTabs>

`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](/docs/sandbox/metrics).
