# Lifecycle and cleanup

`withSandbox()` always attempts `stop()` after the callback succeeds or fails. It also stops sandboxes opened with an existing `id`. Returning a stream, process handle, or preview URL does not keep the sandbox alive. Consume streams inside the callback, or use `create()` and own cleanup for the entire consumer lifetime.

## What stop means

Cleanup is provider-specific. These are the adapter defaults, not a portable pause or delete operation.

| Adapter | Default cleanup | Retention choices |
| --- | --- | --- |
| Local | Removes an automatically created temporary root and local snapshots | Explicit roots remain on disk. Set `keep: true` to retain a temporary root |
| Vercel | Stops the named sandbox. Persistent sandboxes automatically snapshot their filesystem | Native lifecycle and snapshot retention controls remain on `raw` |
| E2B | Kills the sandbox, not a pause | Create a snapshot before cleanup, or manage native pause and resume explicitly |
| Blaxel | Deletes the sandbox, not standby | Use native lifecycle management when retaining a perpetual sandbox |
| Cloudflare | Destroys the container and its local state, including through the HTTP bridge | Persist data externally or configure R2 backups before cleanup |
| Daytona | Stops the sandbox and retains disk, subject to its archive and deletion policies | `deleteOnStop: true` permanently deletes it |
| Modal | Terminates the sandbox | `stop: "detach"` leaves it running. Use volumes or filesystem snapshots for durable data |
| CodeSandbox | Disconnects the session and shuts down the VM, saving files for a clean boot | `stop: "hibernate"` retains resumable memory state, `"disconnect"` only closes the session, and `"delete"` permanently deletes it |

Detached and retained resources may continue to consume quota or incur charges. Provider timeouts, idle policies, and snapshot retention still apply. A background process is not a portable keepalive. Local execution is not an isolation boundary for untrusted code.

## Errors and ownership

Await cleanup in `finally` when managing a sandbox yourself. If both the callback and cleanup fail, `withSandbox()` throws an `AggregateError` containing the work error followed by the cleanup error. Inspect both before retrying. Do not retry a destructive or non-idempotent operation solely because cleanup failed.

For streamed AI output, the [streaming example](https://github.com/dancer/sandbox/blob/main/examples/aisdk.ts) keeps the sandbox alive during iteration, aborts model generation when iteration ends, and awaits cleanup on completion, early exit, or consumer failure.

Snapshots are provider-owned state, not portable archives. Reuse the same provider and account context. Check `snapshotCreate`, `snapshotSource`, `snapshotRestore`, and `snapshotDelete` separately. Save durable snapshot ids before stopping the source; local snapshots do not survive cleanup.

## Provider references

- [Vercel SDK lifecycle](https://vercel.com/docs/sandbox/sdk-reference)
- [E2B persistence](https://docs.e2b.dev/sandbox/persistence)
- [Blaxel sandboxes](https://docs.blaxel.ai/Sandboxes/Overview)
- [Cloudflare lifecycle](https://developers.cloudflare.com/sandbox/api/lifecycle/)
- [Daytona sandboxes](https://www.daytona.io/docs/en/sandboxes/)
- [Modal Sandbox reference](https://modal.com/docs/sdk/js/latest/Sandbox)
- [CodeSandbox lifecycle source](https://github.com/codesandbox/codesandbox-sdk/blob/main/src/Sandboxes.ts)