Sandboxes and lifecycle
With the E2B SDK you create, pause, resume, extend, and kill sandboxes on Kibotos with the same calls as on E2B. This page covers how each lifecycle call behaves on Kibotos, what resources a new sandbox gets, how expiration and Idempotency-Key retries work, and what queries return.
Overview
Each sandbox is a WorkspaceA complete Linux environment for an AI Agent, running in its own MicroVM. Agents read and write files, run processes, and start services in it without affecting the host or other Workspaces. in Kibotos. The lifecycle calls map to these endpoints:
| SDK call | Endpoint | On Kibotos |
|---|---|---|
Sandbox.create | POST /sandboxes | Creates the sandbox with the resources the deployment sets. See Creating a sandbox |
getInfo() | GET /sandboxes/{id} | Returns the configuration in effect. See Sandbox information |
| The SDK's list call | GET /v2/sandboxes | Lists sandboxes, including paused ones |
sandbox.kill() | DELETE /sandboxes/{id} | Deletes the sandbox. A standalone Volume mounted with volumeMounts is kept; the /data Volume of a sandbox created without volumeMounts is deleted with it. See Volumes |
sandbox.pause() | POST /sandboxes/{id}/pause | Cold pause. See Pause and resume |
Sandbox.connect | POST /sandboxes/{id}/connect | Resumes a paused sandbox and only extends its lifetime |
sandbox.setTimeout | POST /sandboxes/{id}/timeout | Sets the expiration time, longer or shorter. See Expiration |
| None | POST /sandboxes/{id}/refreshes | Extends the lifetime by at least 15 seconds |
The examples on this page leave out the connection options (apiKey, domain, apiUrl). Pass them as in the example on E2B compatibility.
Creating a sandbox
Create a sandbox that lives for ten minutes and pauses instead of being deleted when it expires:
import { Sandbox } from 'e2b';
const sandbox = await Sandbox.create('kibotos-minimal', {
timeoutMs: 10 * 60_000,
lifecycle: { onTimeout: 'pause', autoResume: false },
});- The request returns 201 once the sandbox is running, waiting at most 90 seconds. A sandbox that is not running by then returns 502 and may still start. Retry with the same
Idempotency-Keyto wait for that sandbox instead of creating another; see Retries with Idempotency-Key. - A request body larger than 1 MiB returns 400.
- The resources come from the deployment settings, not from the request. See Resources.
- A create that no node can place returns 503. See Insufficient capacity.
- For how long the sandbox lives, see Expiration.
Metadata and option size
The gateway stores metadata and E2B options in ctrl, so they count toward ctrl's total limits:
| Limit | Value | Counted |
|---|---|---|
| Total annotations | 262144 bytes | The raw JSON of the metadata, plus the keys and values of other annotations such as maskRequestHost |
| Total labels | 65536 bytes | The label of each metadata entry (counted as a 48-character digest when the key exceeds 63 characters or does not follow label rules), plus the keys and values of other labels such as the namespace, tokens, and boolean options |
If either limit is exceeded, the create returns 400 and no sandbox is created.
Resources
E2B create requests carry no resource fields. The gateway requests resources for each new sandbox according to deployment settings:
| Resource | Gateway setting | Default |
|---|---|---|
| CPU | KIBOTOS_E2B_DEFAULT_CPU_MILLIS | 1 core |
| Memory | KIBOTOS_E2B_DEFAULT_MEMORY_BYTES | 1 GiB |
| Writable space on the root file system | KIBOTOS_E2B_DEFAULT_DISK_BYTES | 5 GiB |
cpuCount,memoryMB, anddiskSizeMBecho the values the sandbox actually received.diskSizeMBis the writable space confirmed by the node. While the sandbox is starting, or if it was never successfully placed, there is no confirmed value yet.GET /sandboxes/{id}and the list endpoint still return the sandbox, withdiskSizeMBset to0. E2B requires this field to be a non-negative integer and never empty, and upstream also returns0when the value is missing.- The writable space takes up capacity in the node's storage pool from placement onward. Only data actually written takes up disk. Deployments with small storage pools need to lower this value according to the number of sandboxes.
- The settings affect only sandboxes created afterward. The writable space of existing sandboxes does not change.
Deployers set these variables as described in Install the E2B gateway.
Pause and resume
Pause and resume a sandbox:
await sandbox.pause();
const resumed = await Sandbox.connect(sandbox.sandboxId);On Kibotos, a pause is a cold pause: the gateway stops the sandbox with the Kibotos StopWorkspace call, and a resume starts it again with StartWorkspace. Kibotos chose these semantics deliberately.
A pause keeps the sandbox ID and the Volume (mounted at /data by default). A resume starts a new execution, and the envd token and traffic token stay the same.
Warning
Memory and running processes are not kept. Changes to the root file system are kept only on a best-effort basis and cannot be treated as a complete disk snapshot. Write files you need to keep to the Volume.
Pausing
POST /sandboxes/{id}/pause performs a cold pause:
- An empty body,
memory:true, andmemory:falseall perform a cold pause. The SDK'spause()sendsmemory:trueby default; the gateway still performs a cold pause and does not claim to support memory pause. - It returns 204 after confirming the sandbox has stopped.
- A repeated pause returns 409, which the SDK turns into a return value of
false. - A pause clears the expiration time. See When the expiration time changes.
Resuming
The result of POST /sandboxes/{id}/connect depends on the state of the sandbox:
| Sandbox state | Result |
|---|---|
| Running, or still starting | 200 once it is running. The expiration time is only extended; see Expiration |
| Paused, or last failed for a retryable reason | Starts the sandbox, sets the lifetime for this run, waits for the start to finish, and returns 201 |
| Paused, and no node can place it | 503 with a message containing resource_exhausted. The sandbox stays paused; see Insufficient capacity |
| Not running within 90 seconds | 502. The start may still finish; retry with the same Idempotency-Key |
| Failed for a non-retryable reason, or fails while waiting | 409, with a message starting with sandbox failed: |
| Failed and still holding its previous execution, including the rare case where Kibotos could not release the node after a failed placement | 409. Kibotos refuses to start it again. After an operator stops it with kibo workspace stop --force, connect resumes it |
The returned tokens are the same as at creation. As in E2B, connect does not rotate tokens.
Unplanned pauses, restarts, and failures
| Event | Sandbox state | What to do |
|---|---|---|
| The host runs out of memory and the kernel kills the sandbox | Paused | connect resumes it. As with an explicit pause, memory and processes are not kept, and the last writes may be lost |
| The guest shuts itself down | Paused | connect resumes it |
| The guest reboots itself | Stays running; queries and connect succeed as usual | Requests to envd and application ports fail until the guest comes back; retry them. Memory and processes are not kept |
| The sandbox ends unexpectedly for another reason, for example a VMM failure | Failed. It does not recover by itself | connect returns 409, queries and pause return 502, lists do not show it, and retries never succeed. After an operator stops it with kibo workspace stop --force, connect resumes it |
For how Kibotos handles these cases and which backends support them, see Stops and restarts handled by the server.
Insufficient capacity
If no node can place the sandbox at start (for example, all nodes are short on memory), that connect returns 503 with a message containing resource_exhausted. Other endpoints that hit insufficient capacity, such as creating a sandbox or a Volume, also return 503.
- Such a sandbox stays paused, as when a resume fails on upstream E2B: queries return
state: paused, lists show it as paused, and pausing it again returns 409. Once capacity recovers, anotherconnectstarts it, with no operator intervention needed. - A sandbox that could not be placed at creation is also listed as paused. Find it by its metadata, then
connectto start it or delete it. - For the expiration time after a failed start, see After a failed start.
Waking a sandbox through its application ports (autoResume), snapshots, and cloning are not available; see Not supported.
Expiration
Make a running sandbox expire ten minutes from now:
await sandbox.setTimeout(10 * 60_000);Each request sets the expiration time in its own way:
| Request | Effect |
|---|---|
create | Sets the lifetime to the requested timeout. The JS SDK always sends one: 5 minutes unless you set timeoutMs. A request without one gets the gateway's KIBOTOS_E2B_DEFAULT_TIMEOUT, 1 hour by default. The countdown starts when ctrl accepts the command, so it includes creation time |
connect | Only extends. The new endAt is the later of the existing expiration time and "now + the requested timeout". A resume sets the lifetime for this run, counted from when ctrl accepts the start, so start time is included |
timeout | Sets the expiration time to the requested value, which can extend or shorten it |
refreshes | Only extends, by at least 15 seconds (E2B's SandboxTimeoutDefault). A duration that is missing, less than 15 seconds, or not positive is treated as 15 seconds |
Expiration action
| Create option | At the expiration time |
|---|---|
None, or lifecycle.onTimeout: 'kill' | The sandbox is deleted |
autoPause: true (lifecycle.onTimeout: 'pause' in the SDK) | The sandbox is paused instead of deleted |
timeoutandconnectkeep the action set at creation.- Without
autoPause, a resumed sandbox is still deleted when it expires. - Both values of
autoPauseMemoryare treated as a cold pause. Setting onlyautoPauseMemorywithout enablingautoPausereturns 400.
Timeout values
timeoutvalues mean the same as in E2B: a positive integer is a number of seconds, and0and negative numbers both mean expire immediately. E2B expresses this asendAt = now; the gateway uses the shortest positive duration, because ctrl accepts only positive expiration durations.- The request fields of
connectandtimeoutare required in E2B and are treated as0when missing. Onlycreatecan omit the field. - For
connect,0, negative numbers, and missing fields all request "now", so they keep the original value. When the requested timeout is shorter than the remaining time, the original value is kept and the native expiration revision does not advance. This matches E2B:connectonly extends, never shortens. - A deployment can cap the duration of a single request with
KIBOTOS_E2B_MAX_TIMEOUT.createandconnectabove the cap return 400, at the same place where E2B checks the team limit.timeoutandrefreshesare not checked, and E2B does not check them either.
When the expiration time changes
- All of these requests return success only after the native expiration setting has been updated. The
endAtreturned byinspectandlistcomes directly from the native expiration time. - Accessing files and running commands do not change the lifetime.
- The expiration time and the state change of a pause or resume are committed in the same native call. A sandbox that paused successfully has no expiration time, and a delete action set earlier does not fire while it is paused. A sandbox that started successfully always has the expiration time for this run. A failed start is the exception; see After a failed start.
- Expiration actions are carried out by ctrl; the gateway has no timer of its own. Stopping the gateway does not affect expiration times already set.
- While ctrl is temporarily unavailable, extensions fail. After it recovers, it carries out expiration actions at the saved times.
- Once ctrl has started an expiration action, an extension can no longer cancel it.
After a failed start
- A sandbox that had no expiration time before the resume (for example, a paused one) still has none after the failed resume. It is not paused or deleted even after the
timeoutof this request passes. - The start command writes the expiration time for this run as soon as ctrl accepts it, so after a failed start the gateway sends another native call to clear it. If the gateway exits between these two calls, this expiration time remains.
- A sandbox that could not be placed at creation keeps its expiration time from creation and is deleted on expiration according to the settings at creation. If a
connectto it fails, its expiration time is replaced by the one set in that request.
Retries with Idempotency-Key
create, connect, pause, timeout, refreshes, network changes, and delete support the Idempotency-Key header. Requests with the same key must have the same content. A key longer than 256 bytes returns 400.
When a result is unknown, for example after an interruption, retry with the same key. The gateway first asks ctrl whether the command was already committed: if it was, the gateway waits for it to finish and returns the result; only if it was not does the gateway issue it again.
- The value of
Idempotency-Keyis not a Kibotos command IDA stable identifier that the caller generates for every write request. Resending a request with the same command ID does not run the operation twice.. The gateway derives the command ID from it together with the namespace, the operation, and the sandbox. The same value used for different operations yields different command IDs, so you cannot use it to query commands in the native API. - Because command IDs are derived from the key, the gateway stores nothing. None of this depends on local gateway state, so retrying against a different gateway instance works the same way.
- If the same key comes with different request content, ctrl treats it as a conflict and the gateway returns 409.
- Delete and
refreshesalso derive their command IDs from the key, and a retry waits for the same command to finish. - Revision conflicts return 409. The gateway does not read the new revision and retry the old request; make a new request based on the current state.
Sandbox information
GET /sandboxes/{id} and the list endpoint return the configuration actually in effect. The lifecycle fields:
| Field | Echo rules |
|---|---|
lifecycle | {"onTimeout":"kill","autoResume":false} when the expiration action is delete, and onTimeout:"pause" when it is pause. A paused sandbox has no expiration time, and the echo shows the setting the next start will use |
endAt | Comes directly from the native expiration time |
cpuCount, memoryMB, diskSizeMB | The values the sandbox actually received. See Resources |
getInfo() in SDK 2.46.1 keeps these fields and the network, port, and Volume fields. The SDK's list call keeps only volumeMounts; read the other fields through getInfo().
The echo rules of the other fields are on their pages: network fields on Network, allowPublicTraffic and maskRequestHost on Application ports, and volumeMounts on Volumes.
Notes
- The actual state and expiration time of a sandbox are those returned by native API queries.
Next steps
- Commands and files: run commands and transfer files in a sandbox.
- Volumes: keep data across pauses and after a sandbox is deleted.
- Network: control outbound access.
- Expiration: how expiration works for Workspaces.
Migrate from E2B
This page shows how to move a program from E2B to Kibotos: check that what it uses is available, point the SDK at the gateway, and replace what differs. For the full behavior of each call, see the other pages in this section.
Commands and files
The E2B SDK's sandbox.commands.* and sandbox.files.* calls work on Kibotos sandboxes as they do on E2B. This page covers the supported process and file endpoints, which user a request runs as, how paths resolve, and how process streams and directory watches behave.