Manage a sandbox
Open a sandbox you already have, stop or terminate it, and close your local connection.
Create returns a handle. Later calls use that handle, or a handle you open from an id. Pick the row for the call you need.
| You want to | Daytona | Modal |
|---|---|---|
| Open an existing sandbox | get looks the id up | attach takes the id and does not look it up |
| Start, stop, pause, archive | Supported | Rejected before the call |
| Remove the sandbox | delete | Use terminate, or a lifetime set at create time |
| Drop your local connection | close | close |
supports reports whether this provider implements the call. Import LifecycleCapability from @sandbox-kit/sandbox. In Go the constants are sandbox.LifecycleCapabilityStop and the rest of that set. A missing operation fails before the provider is contacted. Daytona can still reject a supported call for one sandbox, and that error comes back to you.
The snippets below assume you already have a client. Build one on the provider page.
Open a sandbox
get loads a sandbox that already exists. refresh loads it again.
const sandbox = await client.get(process.env.DAYTONA_SANDBOX_ID!);
const info = await sandbox.refresh();
console.log(sandbox.id, info.state);Stop, start, pause, and archive
These four calls are Daytona calls. Modal rejects them. stop asks Daytona to stop the sandbox. The sandbox stays available to start again. start brings a stopped or archived sandbox back. pause keeps memory and stops the sandbox. archive moves a retained sandbox into archive storage.
const sandbox = await client.get(process.env.DAYTONA_SANDBOX_ID!);
if (!sandbox.supports(LifecycleCapability.Stop)) {
throw new Error(`sandbox ${sandbox.id} cannot be stopped`);
}
const info = await sandbox.stop({ timeout: 60_000 });
console.log(sandbox.id, info.state);timeout is milliseconds. Pass force: true to request an immediate stop. Check supports the same way before start, pause, or archive.
Daytona decides whether that sandbox can pause, archive, or stop. An ephemeral sandbox can still be removed by the lifetime you set at create time.
Delete
delete removes a Daytona sandbox. Modal rejects it.
const sandbox = await client.get(process.env.DAYTONA_SANDBOX_ID!);
await sandbox.delete({ timeout: 60_000 });Terminate
terminate finishes a Modal sandbox. It is not a stop, and it is not a delete. Daytona rejects it. A maximum lifetime or an idle timeout set at create time also ends the sandbox.
const sandbox = await client.attach(process.env.MODAL_SANDBOX_ID!);
if (!sandbox.supports(LifecycleCapability.Terminate)) {
throw new Error(`sandbox ${sandbox.id} cannot be terminated`);
}
const exit = await sandbox.terminate({ wait: true, timeout: 60_000 });
console.log(sandbox.id, exit.exitCode);wait: true polls until the sandbox exits or the timeout fires. The timeout is how long the client waits. Modal does not receive that duration as its own terminate timeout.
Close the client
await client.close();Prefer await using when the client's lifetime matches the block. Disposing the client closes the connection. Disposing the handle leaves the sandbox as it is.
Read the state
info.state is the portable state. The provider's own string is kept beside it. After a lifecycle call, read the handle again before you treat the sandbox as stopped or gone. A failed call can mean the provider already changed it. See Read an error.