# Daytona Documentation
# https://www.daytona.io/docs
# Generated on: 2026-10-17
# Daytona Documentation
Daytona is a secure and elastic infrastructure for running AI-generated code.
Daytona provides **full composable computers** — [sandboxes](https://www.daytona.io/docs/en/sandboxes.md) — with complete isolation, a dedicated kernel, filesystem, network stack, and allocated vCPU, RAM, and disk.
Sandboxes are the core component of the Daytona platform, spinning up in under 90ms from code to execution and running any code in Python, TypeScript, and JavaScript. Built on OCI/Docker compatibility, massive parallelization, and unlimited persistence, sandboxes deliver consistent, predictable environments for AI, agent and reinforcement learning workflows.
Agents and developers interact with sandboxes programmatically using the SDKs, API, and CLI. Operations span sandbox lifecycle management, filesystem operations, process and code execution, runtime configuration, and more.
Our stateful environment [snapshots](https://www.daytona.io/docs/en/snapshots.md) enable persistent agent operations across sessions, making Daytona the ideal foundation for AI agent architectures.
## Get started
1. Create an account → [app.daytona.io](https://app.daytona.io)
2. Get an API key → [app.daytona.io/dashboard/keys](https://app.daytona.io/dashboard/keys)
3. Install the Python SDK
```bash
pip install daytona
```
4. Create a sandbox and run code
```python
# Import the Daytona SDK
from daytona import Daytona, DaytonaConfig
# Define the configuration
config = DaytonaConfig(api_key="YOUR_API_KEY") # Replace with your API key
# Initialize the Daytona client
daytona = Daytona(config)
# Create the Sandbox instance
sandbox = daytona.create()
# Run code
response = sandbox.process.code_run('print("Hello World")')
print(response.result)
```
3. Install the TypeScript SDK
```bash
npm install @daytona/sdk
```
4. Create a sandbox and run code
```typescript
// Import the Daytona SDK
import { Daytona } from '@daytona/sdk'
// Initialize the Daytona client
const daytona = new Daytona({ apiKey: 'YOUR_API_KEY' }) // Replace with your API key
// Create the Sandbox instance
const sandbox = await daytona.create()
// Run code
const response = await sandbox.process.codeRun('print("Hello World")')
console.log(response.result)
```
3. Install the Ruby SDK
```bash
gem install daytona
```
4. Create a sandbox and run code
```ruby
require 'daytona'
# Initialize the Daytona client
config = Daytona::Config.new(api_key: 'YOUR_API_KEY') # Replace with your API key
# Create the Daytona client
daytona = Daytona::Daytona.new(config)
# Create the Sandbox instance
sandbox = daytona.create
# Run code
response = sandbox.process.code_run(code: 'print("Hello World")')
puts response.result
```
3. Install the Go SDK
```bash
go get github.com/daytona/clients/sdk-go
```
4. Create a sandbox and run code
```go
package main
import (
"context"
"fmt"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
config := &types.DaytonaConfig{
APIKey: "YOUR_API_KEY", // Replace with your API key
}
client, _ := daytona.NewClientWithConfig(config)
ctx := context.Background()
sandbox, _ := client.Create(ctx, nil)
// Run code
result, _ := sandbox.Process.CodeRun(ctx, `print("Hello World")`)
fmt.Println(result.Result)
}
```
3. Install the Java SDK
**Gradle**
Add the Daytona SDK dependency to your `build.gradle.kts`:
```kotlin
dependencies {
implementation("io.daytona:sdk-java:0.1.0")
}
```
**Maven**
Add the Daytona SDK dependency to your `pom.xml`:
```xml
io.daytonasdk-java0.1.0
```
4. Create a sandbox and run code
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.DaytonaConfig;
import io.daytona.sdk.model.ExecuteResponse;
public class Main {
public static void main(String[] args) {
DaytonaConfig config = new DaytonaConfig.Builder()
.apiKey("YOUR_API_KEY")
.build();
try (Daytona daytona = new Daytona(config)) {
Sandbox sandbox = daytona.create();
// Run code
ExecuteResponse response = sandbox.getProcess().codeRun("print(\"Hello World\")");
System.out.println(response.getResult());
}
}
}
```
3. Create a sandbox and run code
```bash
# Create sandbox
curl https://app.daytona.io/api/sandbox \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{}'
# Run code
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/code-run' \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"code": "print(\"Hello World\")",
"language": "python"
}'
```
3. [Install the CLI](https://www.daytona.io/docs/en/tools/cli.md)
4. Create a sandbox and run code
```shell
daytona create --name hello
daytona exec hello -- python3 -c 'print("Hello World")'
```
## Next steps
Daytona provides core platform resources for managing sandboxes, environments, and storage.
## Reference
Daytona provides a comprehensive set of resources for developers and agents to get started and build on the platform. You can find the following resources in the documentation:
- **Daytona SDKs**: [TypeScript](https://www.daytona.io/docs/en/typescript-sdk.md), [Python](https://www.daytona.io/docs/en/python-sdk.md), [Ruby](https://www.daytona.io/docs/en/ruby-sdk.md), [Go](https://www.daytona.io/docs/en/go-sdk.md), [Java](https://www.daytona.io/docs/en/java-sdk.md)
- **Daytona APIs**: [Platform](https://www.daytona.io/docs/en/tools/api.md#daytona) ([OpenAPI](https://www.daytona.io/docs/openapi.json)), [Toolbox](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox) ([OpenAPI](https://www.daytona.io/docs/toolbox-openapi.json)), [Analytics](https://www.daytona.io/docs/en/tools/api.md#daytona-analytics) ([OpenAPI](https://www.daytona.io/docs/analytics-openapi.json))
- **Daytona CLI**: [Mac/Linux/Windows](https://www.daytona.io/docs/en/tools/cli.md)
- **Daytona LLMs**: [llms.txt](https://www.daytona.io/docs/llms.txt), [llms-full.txt](https://www.daytona.io/docs/llms-full.txt)
- **Daytona Sitemap**: [sitemap-0.xml](https://www.daytona.io/docs/sitemap-0.xml)
- **Daytona Skills**: [agent skills](https://www.daytona.io/docs/en/agent-skills.md)
- **Daytona Markdown**: suffix pages with **`.md`** (e.g [sandboxes.md](https://www.daytona.io/docs/en/sandboxes.md))
# Authentication
Daytona API keys authenticate requests to the Daytona API. They are used by the Daytona SDKs and CLI to access and manage resources in your organization.
## Create an API key
Create API keys to authenticate Daytona SDKs, API, and CLI requests.
1. Go to [Daytona Dashboard ↗](https://app.daytona.io/dashboard/keys)
2. Click
3. Enter the name of the API key, set the expiration date, and [select permissions](#permissions--scopes)
4. Click
5. Copy the API key to your clipboard and use it to authenticate requests
```python
from daytona import Daytona, DaytonaConfig
# Using environment variables
daytona = Daytona()
# Using explicit configuration
config = DaytonaConfig(
api_key="YOUR_API_KEY",
api_url="https://app.daytona.io/api",
target="us",
)
daytona = Daytona(config)
```
```typescript
import { Daytona } from '@daytona/sdk'
// Using environment variables
const daytona = new Daytona()
// Using explicit configuration
const daytonaWithConfig = new Daytona({
apiKey: 'YOUR_API_KEY',
apiUrl: 'https://app.daytona.io/api',
target: 'us',
})
```
```ruby
require 'daytona'
# Using environment variables
daytona = Daytona::Daytona.new
# Using explicit configuration
config = Daytona::Config.new(
api_key: 'YOUR_API_KEY',
api_url: 'https://app.daytona.io/api',
target: 'us'
)
daytona = Daytona::Daytona.new(config)
```
```go
import (
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
// Using environment variables
client, _ := daytona.NewClient()
// Using explicit configuration
client, _ = daytona.NewClientWithConfig(&types.DaytonaConfig{
APIKey: "YOUR_API_KEY",
APIUrl: "https://app.daytona.io/api",
Target: "us",
})
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.DaytonaConfig;
// Using environment variables
Daytona daytona = new Daytona();
// Using explicit configuration
DaytonaConfig config = new DaytonaConfig.Builder()
.apiKey("YOUR_API_KEY")
.apiUrl("https://app.daytona.io/api")
.target("us")
.build();
Daytona daytonaWithConfig = new Daytona(config);
```
## Authentication
Daytona supports multiple configuration methods, in order of precedence:
1. Configuration in code
2. Environment variables
3. **`.env`** file
4. Default values
| **Variable** | **Description** |
| ----------------------------- | ---------------------------------------------------------------------------------------- |
| **`DAYTONA_API_KEY`** | Your Daytona API key Required |
| **`DAYTONA_API_URL`** | URL of the Daytona API Default: **`https://app.daytona.io/api`** |
| **`DAYTONA_TARGET`** | Target region for sandboxes Regions: **`us`**, **`eu`** |
| **`DAYTONA_ORGANIZATION_ID`** | Your organization ID Required when authenticating with a JWT token |
| **`DAYTONA_JWT_TOKEN`** | JWT token from **`daytona login`** Used for programmatic account-level operations |
#### **`.env`** file
```bash
DAYTONA_API_KEY=YOUR_API_KEY
DAYTONA_API_URL=https://app.daytona.io/api
DAYTONA_TARGET=us
```
#### Shell
```bash
export DAYTONA_API_KEY=YOUR_API_KEY
export DAYTONA_API_URL=https://app.daytona.io/api
export DAYTONA_TARGET=us
```
```bash
$env:DAYTONA_API_KEY="YOUR_API_KEY"
$env:DAYTONA_API_URL="https://app.daytona.io/api"
$env:DAYTONA_TARGET="us"
```
### JWT tokens
JWT tokens are used to authenticate account-level operations with the Daytona API.
Every JWT-authenticated request must include the `X-Daytona-Organization-ID` header set to your organization ID. JWT tokens expire after a short period.
1. Run [**`daytona login`**](https://www.daytona.io/docs/en/tools/cli.md#daytona-login)
2. Select
3. Complete the sign-in in your browser
4. The CLI saves the access token to `config.json` in the Daytona config directory
Daytona resolves the config directory from the `$DAYTONA_CONFIG_DIR` environment variable. If the variable is not set, Daytona uses the `daytona` folder inside your OS user config directory: `~/.config/daytona` on Linux and `~/Library/Application Support/daytona` on macOS. The active profile stores the token in the `api.token.accessToken` field.
## Permissions & Scopes
Permissions govern the resources in your organization — creating, modifying and deleting sandboxes, snapshots, registries, volumes, regions, runners and keys. Read access to sandboxes, snapshots, registries and regions is always granted and cannot be withheld.
Any member of an organization can create an API key for themselves; key creation is not itself a grantable assignment. A key cannot be created with permissions beyond those its creator holds — a request that includes any scope outside the creator's own assignments is rejected outright rather than trimmed. A member with no assignments can therefore only create a key with no permissions. `manage:api_keys` governs creating keys with an API key rather than interactively.
Sandbox runtime access is not a grantable scope. Any API key valid for an organization can address that organization's running sandboxes through the toolbox — running processes, reading and writing files, opening preview URLs and using the terminal — regardless of the permissions selected for the key, including a key created with none. The organization is the trust boundary for sandbox compute; the scopes below control the resources themselves, not what runs inside them.
Organization usage and spend data is a partial exception. The endpoints that serve usage and cost accept `read:billing`, and also accept `write:sandboxes` for compatibility with keys provisioned before `read:billing` existed. A key that can create sandboxes can therefore also read what those sandboxes cost. Interactive sessions are unaffected — a member's role must grant `read:billing` to view spending in the dashboard.
A sandbox's deletion schedule is a modification rather than a deletion. Setting the auto-delete interval or the time-to-live on an existing sandbox requires `write:sandboxes`, and both values are also accepted when a sandbox is created. When either schedule elapses the sandbox is destroyed and its local filesystem is not recoverable, so a key holding `write:sandboxes` alone can cause sandboxes to be deleted on a schedule. `delete:sandboxes` governs the explicit delete request.
Secrets are mounted into sandboxes by name, so `write:sandboxes` also governs which [secrets](https://www.daytona.io/docs/en/secrets.md) a sandbox receives. A key holding `write:sandboxes` can mount any secret in the organization without holding `manage:secrets`. A secret with no [host allowlist](https://www.daytona.io/docs/en/secrets.md#host-allowlist) is substituted into requests to any host, so such a key can obtain that secret's value. Set a host allowlist on every secret.
Creating a snapshot from an existing sandbox requires `write:sandboxes` rather than `write:snapshots`. The result is an organization [snapshot](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox) like any other: it is listed with the organization's snapshots, other members can create sandboxes from it, it counts against the organization's snapshot quota, and it persists after the source sandbox is deleted. `delete:snapshots` governs its removal.
| **Resource** | **Scope** | **Description** |
| ------------ | ----------------------- | ------------------------ |
| Sandboxes | **`write:sandboxes`** | Create/modify sandboxes, including the deletion schedule and snapshots created from a sandbox |
| | **`delete:sandboxes`** | Delete sandboxes |
| Snapshots | **`write:snapshots`** | Create/modify snapshots |
| | **`delete:snapshots`** | Delete snapshots |
| Registries | **`write:registries`** | Create/modify registries |
| | **`delete:registries`** | Delete registries |
| Volumes | **`read:volumes`** | View volumes |
| | **`write:volumes`** | Create/modify volumes |
| | **`delete:volumes`** | Delete volumes |
| Audit | **`read:audit_logs`** | View audit logs |
| Regions | **`write:regions`** | Create/modify regions |
| | **`delete:regions`** | Delete regions |
| Runners | **`read:runners`** | View runners |
| | **`write:runners`** | Create/modify runners |
| | **`delete:runners`** | Delete runners |
| API Keys | **`manage:api_keys`** | Create, list, and delete API keys using an API key |
| Secrets | **`manage:secrets`** | Create, update, and delete [secrets](https://www.daytona.io/docs/en/secrets.md) |
| SSO | **`manage:sso`** | Generate Admin Portal links, list SSO connections, and manage legacy identity providers for [organization SSO](https://www.daytona.io/docs/en/sso.md) |
| Limits | **`read:limits`** | View organization limits and quota usage |
| Billing | **`read:billing`** | View organization usage, spend, wallet, and invoices |
| | **`manage:billing`** | Manage billing settings and payment methods |
## List API keys
List API keys for the current user or organization.
When authenticated with a JWT, organization owners see all keys in the organization and other users see only their own keys. When authenticated with a manager API key, the response is limited to keys that manager key created. See [Managed API keys](#managed-api-keys).
```bash
curl 'https://app.daytona.io/api/api-keys' \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN'
```
## Get current API key
Get details of the API key used to authenticate the current request.
```bash
curl 'https://app.daytona.io/api/api-keys/current' \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Authorization: Bearer YOUR_API_KEY'
```
## Get API key
Get a single API key by name.
```bash
curl 'https://app.daytona.io/api/api-keys/my-api-key' \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN'
```
## Delete API key
Delete an API key.
The key is revoked immediately and cannot be recovered.
1. Go to [Daytona Dashboard ↗](https://app.daytona.io/dashboard/keys)
2. Click next to the API key you want to delete
3. Confirm the revocation
```bash
curl 'https://app.daytona.io/api/api-keys/my-api-key' \
--request DELETE \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN'
```
## Delete API key for user
Delete an API key for a specific user.
This endpoint requires JWT authentication and is available to the key or organization owner.
```bash
curl 'https://app.daytona.io/api/api-keys/{userId}/my-api-key' \
--request DELETE \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN'
```
## Managed API keys
**Contact Daytona to enable this feature for your organization.**
Daytona provides Managed API keys to create and manage API keys programmatically. Managed API keys let a manager key mint, list, and delete child API keys without a JWT session.
A manager key is an API key with the **`manage:api_keys`** permission. Use this when an application or service needs to issue scoped keys to tenants or workloads at runtime.
When authenticated with a manager key:
- child key permissions must be a subset of the manager key's permissions
- a manager key can only list and delete keys it created; it cannot access other keys
- child keys cannot manage other keys unless you explicitly grant them **`manage:api_keys`**
### Create a manager key
Create a manager key.
1. Go to [Daytona Dashboard ↗](https://app.daytona.io/dashboard/keys)
2. Click
3. Enter the key name, set the expiration date, and enable **Manage API keys**
4. Select the resource scopes child keys may use
5. Click
6. Copy the API key to your clipboard
```bash
curl 'https://app.daytona.io/api/api-keys' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN' \
--data '{
"name": "Manager Key",
"permissions": ["manage:api_keys", "write:sandboxes", "delete:sandboxes"]
}'
```
### Create a child key
Create a child key.
```bash
curl 'https://app.daytona.io/api/api-keys' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_MANAGER_API_KEY' \
--data '{
"name": "tenant-a-key",
"permissions": ["write:sandboxes", "delete:sandboxes"]
}'
```
The response includes the full child key value. Store it immediately. After creation, only a masked value is available when listing keys.
### List child keys
List child keys.
```bash
curl 'https://app.daytona.io/api/api-keys' \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Authorization: Bearer YOUR_MANAGER_API_KEY'
```
### Delete a child key
Delete a child key.
1. Authenticate with the manager key
2. Send a `DELETE` request to `/api-keys/{name}`
A manager key can only delete keys it created
```bash
curl 'https://app.daytona.io/api/api-keys/tenant-a-key' \
--request DELETE \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Authorization: Bearer YOUR_MANAGER_API_KEY'
```
# Sandboxes
Daytona provides **full composable computers** — **sandboxes** — for AI agents. Sandboxes are isolated runtime environments you can manage programmatically to run code. Each sandbox runs in isolation, giving it a dedicated kernel, filesystem, network stack, and allocated vCPU, RAM, and disk. Agents and developers get access to a full composable computer where they can install packages, run servers, compile code, and manage processes.
Sandboxes run as **Linux containers** by default. Daytona also provides [VM sandboxes](#vm-sandboxes) with a dedicated **Linux VM** or **Windows** operating system, [GPU sandboxes](#gpu-sandboxes) with **NVIDIA** and **AMD GPU** acceleration for model inference, fine-tuning, and GPU-accelerated compute, and [macOS sandboxes](#macos-sandboxes) running on dedicated Apple silicon hardware.
## Create sandboxes
Create a sandbox.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Click
```python
from daytona import Daytona
daytona = Daytona()
sandbox = daytona.create()
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create()
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
_, _ = client.Create(ctx, nil)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Sandbox sandbox = daytona.create();
}
}
}
```
```bash
daytona create [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{}'
```
### Snapshots
Create a sandbox from a [default snapshot](https://www.daytona.io/docs/en/snapshots.md#default-snapshots).
| **Snapshot** | **vCPU** | **Memory** | **Storage** | **GPU** | **Sandbox Class** |
| ----------------------- | -------- | ---------- | ----------- | ------- | ----------------- |
| **`daytona-small`** | 1 | 1GiB | 3GiB | | Container |
| **`daytona-medium`** | 2 | 4GiB | 8GiB | | Container |
| **`daytona-large`** | 4 | 8GiB | 10GiB | | Container |
| **`daytona-gpu`** | 1 | 1GiB | 1GiB | 1 | GPU |
| **`daytona-vm-small`** | 1 | 1GiB | 3GiB | | Linux VM |
| **`daytona-vm-medium`** | 2 | 4GiB | 8GiB | | Linux VM |
| **`daytona-vm-large`** | 4 | 8GiB | 10GiB | | Linux VM |
| **`windows-small`** | 1 | 4GiB | 30GiB | | Windows |
| **`windows-medium`** | 2 | 8GiB | 50GiB | | Windows |
| **`windows-large`** | 4 | 16GiB | 50GiB | | Windows |
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Select a
4. Click
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="daytona-small",
)
)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: 'daytona-small',
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'daytona-small'
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
params := types.SnapshotParams{
Snapshot: "daytona-small",
}
_, _ = client.Create(ctx, params)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("daytona-small");
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create --snapshot daytona-small
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "daytona-small"
}'
```
### Images
Create a sandbox from an image. The image must be publicly accessible and include either a tag or a digest (e.g. **`ubuntu:22.04`**); the **`latest`**/**`lts`**/**`stable`** tags are not supported.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Enter an (e.g. **`ubuntu:22.04`**) as the source for the sandbox
4. Click
```python
from daytona import Daytona, CreateSandboxFromImageParams
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromImageParams(
image="ubuntu:22.04",
)
)
```
```typescript
import { Daytona, Image } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
image: Image.base('ubuntu:22.04'),
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromImageParams.new(
image: Daytona::Image.base('ubuntu:22.04')
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
_, _ = client.Create(ctx, types.ImageParams{
Image: "ubuntu:22.04",
})
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromImageParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromImageParams params = new CreateSandboxFromImageParams();
params.setImage("ubuntu:22.04");
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"buildInfo": {
"dockerfileContent": "FROM ubuntu:22.04\n"
}
}'
```
### Resources
Create a sandbox with custom resources.
Sandboxes have **1 vCPU**, **1GB RAM**, and **3GiB disk** by default. Organizations get a maximum sandbox resource limit of **4 vCPUs**, **8GB RAM**, and **10GB disk**.
| **Resource** | **Unit** | **Default** | **Minimum** | **Maximum** |
| ------------ | -------- | ----------- | ----------- | ----------- |
| CPU | vCPU | **`1`** | **`1`** | **`4`** |
| Memory | GiB | **`1`** | **`1`** | **`8`** |
| Disk | GiB | **`3`** | **`1`** | **`10`** |
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Enter an (e.g. **`ubuntu:22.04`**) as the source for the sandbox
4. Set (**`cpu`**, **`memory`**, **`disk`**) to the values within your organization's limits
5. Click
```python
from daytona import Daytona, CreateSandboxFromImageParams, Image, Resources
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromImageParams(
image="ubuntu:22.04",
resources=Resources(cpu=2, memory=4, disk=8),
)
)
```
```typescript
import { Daytona, Image } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
image: Image.base('ubuntu:22.04'),
resources: { cpu: 2, memory: 4, disk: 8 },
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromImageParams.new(
image: Daytona::Image.base('ubuntu:22.04'),
resources: Daytona::Resources.new(
cpu: 2,
memory: 4,
disk: 8
)
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
_, _ = client.Create(ctx, types.ImageParams{
Image: "ubuntu:22.04",
Resources: &types.Resources{
CPU: 2,
Memory: 4,
Disk: 8,
},
})
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromImageParams;
import io.daytona.sdk.model.Resources;
final class CreateSandboxResources {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromImageParams params = new CreateSandboxFromImageParams();
params.setImage("ubuntu:22.04");
Resources resources = new Resources();
resources.setCpu(2);
resources.setMemory(4);
resources.setDisk(8);
params.setResources(resources);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create --cpu 2 --memory 4 --disk 8
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"buildInfo": {
"dockerfileContent": "FROM ubuntu:22.04\n"
},
"cpu": 2,
"memory": 4,
"disk": 8
}'
```
### Languages
Create a sandbox with a specific language runtime.
Daytona sandboxes support **Python**, **TypeScript**, and **JavaScript** programming language runtimes for direct code execution inside the sandbox. The language parameter controls which programming language runtime is used for the sandbox. If omitted, it defaults to Python.
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
# Python runtime (default)
sandbox = daytona.create(CreateSandboxFromSnapshotParams(language="python"))
response = sandbox.process.code_run('print("Hello from Python")')
print(response.result)
# TypeScript runtime
sandbox = daytona.create(CreateSandboxFromSnapshotParams(language="typescript"))
response = sandbox.process.code_run('console.log("Hello from TypeScript")')
print(response.result)
# JavaScript runtime
sandbox = daytona.create(CreateSandboxFromSnapshotParams(language="javascript"))
response = sandbox.process.code_run('console.log("Hello from JavaScript")')
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
// Python runtime (default)
let sandbox = await daytona.create({ language: 'python' })
let response = await sandbox.process.codeRun('print("Hello from Python")')
console.log(response.result)
// TypeScript runtime
sandbox = await daytona.create({ language: 'typescript' })
response = await sandbox.process.codeRun('console.log("Hello from TypeScript")')
console.log(response.result)
// JavaScript runtime
sandbox = await daytona.create({ language: 'javascript' })
response = await sandbox.process.codeRun('console.log("Hello from JavaScript")')
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
# Python runtime (default)
sandbox = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::PYTHON
))
response = sandbox.process.code_run(code: 'print("Hello from Python")')
puts response.result
# TypeScript runtime
sandbox = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::TYPESCRIPT
))
response = sandbox.process.code_run(code: 'console.log("Hello from TypeScript")')
puts response.result
# JavaScript runtime
sandbox = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::JAVASCRIPT
))
response = sandbox.process.code_run(code: 'console.log("Hello from JavaScript")')
puts response.result
```
```go
package main
import (
"context"
"fmt"
"log"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// Python runtime (default)
sandbox, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{Language: types.CodeLanguagePython},
})
if err != nil {
log.Fatal(err)
}
result, err := sandbox.Process.CodeRun(ctx, `print("Hello from Python")`)
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Result)
// TypeScript runtime
sandbox, err = client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{Language: types.CodeLanguageTypeScript},
})
if err != nil {
log.Fatal(err)
}
result, err = sandbox.Process.CodeRun(ctx, `console.log("Hello from TypeScript")`)
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Result)
// JavaScript runtime
sandbox, err = client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{Language: types.CodeLanguageJavaScript},
})
if err != nil {
log.Fatal(err)
}
result, err = sandbox.Process.CodeRun(ctx, `console.log("Hello from JavaScript")`)
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Result)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
Daytona daytona = new Daytona();
// Python runtime (default)
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setLanguage("python");
Sandbox sandbox = daytona.create(params);
ExecuteResponse response = sandbox.process.codeRun("print(\"Hello from Python\")");
System.out.println(response.getResult());
// TypeScript runtime
params = new CreateSandboxFromSnapshotParams();
params.setLanguage("typescript");
sandbox = daytona.create(params);
response = sandbox.process.codeRun("console.log(\"Hello from TypeScript\")");
System.out.println(response.getResult());
// JavaScript runtime
params = new CreateSandboxFromSnapshotParams();
params.setLanguage("javascript");
sandbox = daytona.create(params);
response = sandbox.process.codeRun("console.log(\"Hello from JavaScript\")");
System.out.println(response.getResult());
```
```bash
# Python runtime (default)
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"labels": {
"code-toolbox-language": "python"
}
}'
# TypeScript runtime
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"labels": {
"code-toolbox-language": "typescript"
}
}'
# JavaScript runtime
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"labels": {
"code-toolbox-language": "javascript"
}
}'
```
### Environment variables
Create a sandbox with environment variables. Use [secrets](https://www.daytona.io/docs/en/secrets.md) for API keys, tokens, and passwords.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Click and enter key-value pairs for each environment variable
4. Click
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
env_vars={"DEBUG": "true", "LOG_LEVEL": "info"},
))
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
envVars: { DEBUG: 'true', LOG_LEVEL: 'info' },
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(
env_vars: { 'DEBUG' => 'true', 'LOG_LEVEL' => 'info' }
))
```
```go
package main
import (
"context"
"log"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
sandbox, err := client.Create(context.Background(), types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
EnvVars: map[string]string{
"DEBUG": "true",
"LOG_LEVEL": "info",
},
},
})
if err != nil {
log.Fatal(err)
}
log.Printf("Sandbox ID: %s", sandbox.ID)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import java.util.Map;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setEnvVars(Map.of("DEBUG", "true", "LOG_LEVEL", "info"));
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create -e DEBUG=true -e LOG_LEVEL=info
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"env": {
"DEBUG": "true",
"LOG_LEVEL": "info"
}
}'
```
### Regions
Create a sandbox in a specific [region](https://www.daytona.io/docs/en/regions.md).
| **Region** | **Target** |
| ------------- | ---------- |
| United States | **`us`** |
| Europe | **`eu`** |
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Set to the target region you want to create the sandbox in
4. Click
```python
from daytona import Daytona, DaytonaConfig
daytona = Daytona(DaytonaConfig(target="us"))
sandbox = daytona.create()
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona({ target: 'us' })
const sandbox = await daytona.create()
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new(Daytona::Config.new(target: 'us'))
sandbox = daytona.create
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClientWithConfig(&types.DaytonaConfig{
Target: "us",
})
ctx := context.Background()
_, _ = client.Create(ctx, nil)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.DaytonaConfig;
import io.daytona.sdk.Sandbox;
public class App {
public static void main(String[] args) {
DaytonaConfig config = new DaytonaConfig.Builder()
.apiKey(System.getenv("DAYTONA_API_KEY"))
.target("us")
.build();
try (Daytona daytona = new Daytona(config)) {
Sandbox sandbox = daytona.create();
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"target": "us"
}'
```
### VM sandboxes
Daytona provides **VM sandboxes** for workloads that require a full virtual machine with a dedicated **Linux VM** or **Windows** operating system. VM sandboxes are distinct from container sandboxes and support VM-only capabilities:
- [Fork sandboxes](#fork-sandboxes)
- [Pause and resume sandboxes](#pause--resume-sandboxes)
- [Create snapshot from sandbox](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox)
- [Nested virtualization](#nested-virtualization) (Linux VM only)
:::note[Limitations]
VM sandboxes can currently only be created from existing VM snapshots. Dynamic builds through the declarative builder are supported for container sandboxes only.
:::
Create a Linux VM sandbox.
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click
3. Enter (e.g. **`my-vm-snapshot`**)
4. Enter an (e.g. **`ubuntu:22.04`**)
5. Select : **`LINUX VM`**
6. Click
7. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
8. Click
9. Select a VM (e.g. **`my-vm-snapshot`**)
10. Click
```python
from daytona import (
Daytona,
CreateSnapshotParams,
CreateSandboxFromSnapshotParams,
SandboxClass,
)
daytona = Daytona()
# 1. Create a VM snapshot (Linux VM class)
daytona.snapshot.create(
CreateSnapshotParams(
name="my-vm-snapshot",
image="ubuntu:22.04",
sandbox_class=SandboxClass.LINUX_VM,
)
)
# 2. Create a VM sandbox from the snapshot
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="my-vm-snapshot"))
```
```typescript
import { Daytona, SandboxClass } from "@daytona/sdk";
const daytona = new Daytona();
// 1. Create a VM snapshot (Linux VM class)
await daytona.snapshot.create({
name: "my-vm-snapshot",
image: "ubuntu:22.04",
sandboxClass: SandboxClass.LINUX_VM,
});
// 2. Create a VM sandbox from the snapshot
const sandbox = await daytona.create({ snapshot: "my-vm-snapshot" });
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
# 1. Create a VM snapshot (Linux VM class)
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(
name: 'my-vm-snapshot',
image: 'ubuntu:22.04',
sandbox_class: DaytonaApiClient::SandboxClass::LINUX_VM
)
)
# 2. Create a VM sandbox from the snapshot
sandbox = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(snapshot: 'my-vm-snapshot'))
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
// 1. Create a VM snapshot (Linux VM class)
sandboxClass := types.SandboxClassLinuxVM
_, logCh, _ := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "my-vm-snapshot",
Image: "ubuntu:22.04",
SandboxClass: &sandboxClass,
})
for range logCh {
}
// 2. Create a VM sandbox from the snapshot
_, _ = client.Create(ctx, types.SnapshotParams{
Snapshot: "my-vm-snapshot",
})
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.api.client.model.SandboxClass;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
// 1. Create a VM snapshot (Linux VM class)
daytona.snapshot().create("my-vm-snapshot", "ubuntu:22.04", SandboxClass.LINUX_VM);
// 2. Create a VM sandbox from the snapshot
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my-vm-snapshot");
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
# 1. Create a VM snapshot (Linux VM class)
daytona snapshot create my-vm-snapshot --image ubuntu:22.04 --sandbox-class linux-vm
# 2. Create a VM sandbox from the snapshot
daytona create --snapshot my-vm-snapshot
```
```bash
# 1. Create a VM snapshot (Linux VM class)
curl https://app.daytona.io/api/snapshots \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-vm-snapshot",
"imageName": "ubuntu:22.04",
"sandboxClass": "linux-vm"
}'
# 2. Create a VM sandbox from the snapshot
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "my-vm-snapshot"
}'
```
Create a Linux VM sandbox from a default snapshot.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Select a VM : **`daytona-vm-small`**, **`daytona-vm-medium`**, **`daytona-vm-large`**
4. Click
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="daytona-vm-small"))
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({ snapshot: 'daytona-vm-small' })
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'daytona-vm-small'
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
params := types.SnapshotParams{
Snapshot: "daytona-vm-small",
}
_, _ = client.Create(ctx, params)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("daytona-vm-small");
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create --snapshot daytona-vm-small
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "daytona-vm-small"
}'
```
Create a Windows sandbox.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Select a Windows : **`windows-small`**, **`windows-medium`**, **`windows-large`**
4. Click
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="windows-small",
)
)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: 'windows-small',
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'windows-small'
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
params := types.SnapshotParams{
Snapshot: "windows-small",
}
_, _ = client.Create(ctx, params)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("windows-small");
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create --snapshot windows-small
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "windows-small"
}'
```
#### Nested virtualization
Daytona provides nested virtualization for Linux VM sandboxes. Nested virtualization exposes KVM (`/dev/kvm`) inside the guest, so the sandbox can run its own virtual machines with hardware acceleration.
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="daytona-vm-small",
kvm=True,
)
)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: 'daytona-vm-small',
kvm: true,
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'daytona-vm-small',
kvm: true
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
params := types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
Kvm: true,
},
Snapshot: "daytona-vm-small",
}
_, _ = client.Create(ctx, params)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("daytona-vm-small");
params.setKvm(true);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create --snapshot daytona-vm-small --kvm
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "daytona-vm-small",
"kvm": true
}'
```
### macOS sandboxes
Daytona provides macOS sandboxes through the [use.computer](https://use.computer) platform.
1. Go to [use.computer ↗](https://use.computer/)
2. Obtain an API key
3. Reserve a Mac
4. Use the API key to create a macOS sandbox
```bash
curl 'https://api.use.computer/v1/sandboxes' \
--request POST \
--header 'Authorization: Bearer YOUR_USE_COMPUTER_API_KEY' \
--header 'Content-Type: application/json' \
--data '{"type":"macos"}'
```
See the [use.computer API documentation ↗](https://use.computer/docs) for Computer Use, VNC, SSH, screenshots, and other macOS sandbox operations.
### GPU sandboxes
Daytona provides **GPU sandboxes** for workloads that require GPU acceleration, such as model inference, fine-tuning, and GPU-accelerated compute. GPU sandboxes are ephemeral and support up to **8 GPUs**. Resource limits scale with the number of GPU units: each GPU adds up to **16 vCPUs**, **192GB RAM**, and **512GB disk**. Supported **NVIDIA** and **AMD** GPU types:
- **NVIDIA H100**
- **NVIDIA H200**
- **NVIDIA B200**
- **NVIDIA B300**
- **NVIDIA RTX Pro 6000**
- **NVIDIA RTX 4090**
- **NVIDIA RTX 5090**
- **AMD Instinct MI355X**
GPU sandboxes are on-demand. See [spot GPU sandboxes](#spot-gpu-sandboxes) for preemptible GPU capacity. Use the [GPU capacity](#gpu-capacity) endpoint to check available GPU units before creating a sandbox. When no GPU capacity is available, a GPU sandbox waits until capacity frees up.
GPU sandboxes on shared regions run in the [Earth region](https://www.daytona.io/docs/en/regions.md#earth-region). Daytona selects the shared region, ignores the requested target, and reports `earth` as the sandbox `target`. GPU sandboxes on [dedicated](https://www.daytona.io/docs/en/regions.md#dedicated-regions) and [custom](https://www.daytona.io/docs/en/regions.md#custom-regions) regions keep their real region ID. To run GPU sandboxes in a specific geographic location, contact [support@daytona.io](mailto:support@daytona.io).
Create a GPU sandbox with custom GPU resources: units and types.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Enter an (e.g. **`pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime`**)
4. Set to the number of GPU units (e.g. **`1`**)
5. Select : **`H100`**, **`H200`**, **`B200`**, **`B300`**, **`RTX-PRO-6000`**, **`RTX-4090`**, **`RTX-5090`**, **`MI355X`**
The GPU type field accepts a single value or an ordered list of preferred types.
Daytona uses the first available type in the order you provide. This lets you fall back from a preferred GPU to an alternative when the first choice is not available.
```python
from daytona import Daytona, CreateSandboxFromImageParams, Resources, GpuType
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromImageParams(
image="pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime",
auto_delete_interval=0,
resources=Resources(
gpu=1,
gpu_type=[GpuType.H100, GpuType.RTX_PRO_6000],
),
)
)
```
```typescript
import { Daytona, GpuType } from "@daytona/sdk";
const daytona = new Daytona()
const sandbox = await daytona.create({
image: "pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime",
autoDeleteInterval: 0,
resources: {
gpu: 1,
gpuType: [GpuType.H100, GpuType.RTX_PRO_6000],
},
});
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromImageParams.new(
image: 'pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime',
auto_delete_interval: 0,
resources: Daytona::Resources.new(
gpu: 1,
gpu_type: [Daytona::GpuType::H100, Daytona::GpuType::RTX_PRO_6000]
)
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
autoDelete := 0
_, _ = client.Create(ctx, types.ImageParams{
Image: "pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime",
SandboxBaseParams: types.SandboxBaseParams{
AutoDeleteInterval: &autoDelete,
},
Resources: &types.Resources{
GPU: 1,
GpuType: []types.GpuType{types.GpuTypeH100, types.GpuTypeRtxPro6000},
},
})
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromImageParams;
import io.daytona.sdk.model.Resources;
import io.daytona.api.client.model.GpuType;
import java.util.List;
final class CreateGpuSandbox {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromImageParams params = new CreateSandboxFromImageParams();
params.setImage("pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime");
params.setAutoDeleteInterval(0);
Resources resources = new Resources();
resources.setGpu(1);
resources.setGpuType(List.of(GpuType.H100, GpuType.RTX_PRO_6000));
params.setResources(resources);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"buildInfo": {
"dockerfileContent": "FROM pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime\n"
},
"autoDeleteInterval": 0,
"gpu": 1,
"gpuType": ["H100", "RTX-PRO-6000"]
}'
```
Create a GPU sandbox from a default snapshot.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Select a GPU : **`daytona-gpu`**
4. Click
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="daytona-gpu",
auto_delete_interval=0,
),
)
```
```typescript
import { Daytona } from "@daytona/sdk";
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: "daytona-gpu",
ephemeral: true,
});
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'daytona-gpu',
ephemeral: true
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
params := types.SnapshotParams{
Snapshot: "daytona-gpu",
SandboxBaseParams: types.SandboxBaseParams{
Ephemeral: true,
},
}
_, _ = client.Create(ctx, params)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("daytona-gpu");
params.setAutoDeleteInterval(0);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create --snapshot daytona-gpu --auto-delete 0
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "daytona-gpu",
"autoDeleteInterval": 0
}'
```
### Spot GPU sandboxes
Spot GPU sandboxes are preemptible and run on GPU capacity that is not being used by reserved (on-demand) sandboxes. They support the same GPU types, unit counts, and resource limits as on-demand GPU sandboxes.
A spot GPU sandbox can be terminated at any time without notice when an on-demand GPU sandbox needs the capacity. Daytona does not send a preemption warning or a dedicated preemption webhook. Design workloads to tolerate immediate interruption.
When a spot GPU sandbox is destroyed, normal [sandbox lifecycle state](#sandbox-lifecycle) updates still apply. Daytona records when the preemption occurred with a `spotEvictedAt` timestamp. Sandboxes destroyed by spot GPU preemption remain retrievable for 24 hours.
Spot GPU sandboxes do not count against the organization's GPU quota. Available GPU capacity is the only limit. If no spot GPU is available, create fails immediately.
Create a spot GPU sandbox with custom GPU resources: units and types.
```python
from daytona import Daytona, CreateSandboxFromImageParams, Resources, GpuType
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromImageParams(
image="pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime",
auto_delete_interval=0,
spot=True,
resources=Resources(gpu=1, gpu_type=GpuType.H100),
)
)
```
```typescript
import { Daytona, GpuType } from "@daytona/sdk";
const daytona = new Daytona()
const sandbox = await daytona.create({
image: "pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime",
autoDeleteInterval: 0,
spot: true,
resources: {
gpu: 1,
gpuType: GpuType.H100,
},
});
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromImageParams.new(
image: 'pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime',
auto_delete_interval: 0,
spot: true,
resources: Daytona::Resources.new(gpu: 1, gpu_type: Daytona::GpuType::H100)
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
autoDelete := 0
_, _ = client.Create(ctx, types.ImageParams{
Image: "pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime",
SandboxBaseParams: types.SandboxBaseParams{
AutoDeleteInterval: &autoDelete,
Spot: true,
},
Resources: &types.Resources{
GPU: 1,
GpuType: []types.GpuType{types.GpuTypeH100},
},
})
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromImageParams;
import io.daytona.sdk.model.Resources;
import io.daytona.api.client.model.GpuType;
import java.util.List;
final class CreateSpotGpuSandbox {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromImageParams params = new CreateSandboxFromImageParams();
params.setImage("pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime");
params.setAutoDeleteInterval(0);
params.setSpot(true);
Resources resources = new Resources();
resources.setGpu(1);
resources.setGpuType(List.of(GpuType.H100));
params.setResources(resources);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"buildInfo": {
"dockerfileContent": "FROM pytorch/pytorch:2.11.0-cuda12.8-cudnn9-runtime\n"
},
"autoDeleteInterval": 0,
"spot": true,
"gpu": 1,
"gpuType": ["H100"]
}'
```
Create a spot GPU sandbox from a default snapshot.
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="daytona-gpu",
auto_delete_interval=0,
spot=True,
),
)
```
```typescript
import { Daytona } from "@daytona/sdk";
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: "daytona-gpu",
autoDeleteInterval: 0,
spot: true,
});
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'daytona-gpu',
auto_delete_interval: 0,
spot: true
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
autoDelete := 0
params := types.SnapshotParams{
Snapshot: "daytona-gpu",
SandboxBaseParams: types.SandboxBaseParams{
AutoDeleteInterval: &autoDelete,
Spot: true,
},
}
_, _ = client.Create(ctx, params)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("daytona-gpu");
params.setAutoDeleteInterval(0);
params.setSpot(true);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "daytona-gpu",
"autoDeleteInterval": 0,
"spot": true
}'
```
### GPU capacity
Get the available GPU capacity. The response lists available GPU units by GPU type, split into on-demand and spot. Capacity is aggregated across all shared regions in the [Earth region](https://www.daytona.io/docs/en/regions.md#earth-region) and changes as sandboxes are created and deleted. The response does not reserve capacity and does not apply the organization's GPU quota. On-demand GPU sandboxes remain subject to the quota. Spot GPU sandboxes are limited only by available capacity.
```bash
curl 'https://app.daytona.io/api/organizations/ORGANIZATION_ID/gpu-capacity' \
--header 'Authorization: Bearer YOUR_API_KEY'
```
Response example:
```json
{
"observedAt": "2026-09-14T17:31:05.412Z",
"capacity": [
{ "gpuType": "H100", "availableOnDemand": 12, "availableSpot": 4 },
{ "gpuType": "H200", "availableOnDemand": 0, "availableSpot": 0 },
{ "gpuType": "RTX-PRO-6000", "availableOnDemand": 6, "availableSpot": 6 }
]
}
```
| **Field** | **Description** |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **`observedAt`** | Time the capacity was calculated. Responses are cached for up to five seconds. |
| **`capacity`** | One entry per GPU type. |
| **`gpuType`** | **`H100`**, **`H200`**, **`B200`**, **`B300`**, **`RTX-PRO-6000`**, **`RTX-4090`**, **`RTX-5090`**, or **`MI355X`**. |
| **`availableOnDemand`** | GPU units available to on-demand GPU sandboxes. Includes units held by spot GPU sandboxes that Daytona can reclaim. |
| **`availableSpot`** | GPU units currently free for spot GPU sandboxes. |
### Ephemeral sandboxes
Create an ephemeral sandbox. Ephemeral sandboxes are automatically deleted when stopped.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Set or set the [auto-delete interval](#auto-delete-interval) to **`0`**
4. Click
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
params = CreateSandboxFromSnapshotParams(
ephemeral=True,
auto_stop_interval=5,
)
sandbox = daytona.create(params)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
ephemeral: true,
autoStopInterval: 5,
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
params = Daytona::CreateSandboxFromSnapshotParams.new(
ephemeral: true,
auto_stop_interval: 5
)
sandbox = daytona.create(params)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
autoStopInterval := 5
params := types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
Language: types.CodeLanguagePython,
Ephemeral: true,
AutoStopInterval: &autoStopInterval,
},
}
_, _ = client.Create(ctx, params)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setAutoDeleteInterval(0);
params.setAutoStopInterval(5);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create --auto-delete 0 --auto-stop 5
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"autoDeleteInterval": 0,
"autoStopInterval": 5
}'
```
### Linked sandboxes
Create a linked sandbox. Linked sandboxes attach ephemeral child sandboxes to a parent. Daytona schedules each child on the same runner as the parent and joins them into a shared link network so the group can communicate over local connections.
- **Lifecycle**
Linked sandboxes are always ephemeral and cannot be persisted or resumed after stop. The [auto-delete interval](#auto-delete-interval) must be exactly `0` on create; this is enforced, not a default. The [auto-stop interval](#auto-stop-interval) sets the idle period in minutes after which the child sandbox stops. Once stopped, linked children are auto-deleted. Deleting the parent deletes all of its linked children (cascade). One parent may have many linked children (1:N).
- **Networking**
Linked sandboxes share an internal link network. Connections work in both directions: the parent can reach each child and each child can reach the parent. Every sandbox on the link network is registered under its sandbox name and ID as DNS aliases, so either works as the host. For example: `telnet LINKED_SANDBOX_ID 5555` from the parent reaches port `5555` on the linked child sandbox.
1. Create a parent sandbox
2. Create one or more child sandboxes that reference the parent's sandbox ID.
This records the relationship on the child sandbox as the linked sandbox ID. Omitting the linked sandbox parameter yields an unlinked sandbox.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
parent = daytona.create()
child = daytona.create(
CreateSandboxFromSnapshotParams(
linked_sandbox=parent.id,
ephemeral=True,
)
)
# The link network registers each sandbox under its name as a DNS alias
response = child.process.exec(f"curl http://{parent.name}:3000/")
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const parent = await daytona.create()
const child = await daytona.create({
linkedSandbox: parent.id,
ephemeral: true,
})
// The link network registers each sandbox under its name as a DNS alias
const response = await child.process.executeCommand(
`curl http://${parent.name}:3000/`
)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
parent = daytona.create
child = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
linked_sandbox: parent.id,
ephemeral: true
)
)
# The link network registers each sandbox under its name and ID as DNS aliases.
# The Ruby SDK does not expose the sandbox name, so address the parent by ID.
response = child.process.exec(command: "curl http://#{parent.id}:3000/")
```
```go
package main
import (
"context"
"fmt"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
parent, _ := client.Create(ctx, types.SnapshotParams{})
child, _ := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
LinkedSandbox: parent.ID,
Ephemeral: true,
},
})
// The link network registers each sandbox under its name as a DNS alias
response, _ := child.Process.ExecuteCommand(ctx, fmt.Sprintf("curl http://%s:3000/", parent.Name))
fmt.Println(response.Result)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Sandbox parent = daytona.create();
CreateSandboxFromSnapshotParams childParams = new CreateSandboxFromSnapshotParams();
childParams.setLinkedSandbox(parent.getId());
childParams.setAutoDeleteInterval(0); // linked sandboxes must be ephemeral
Sandbox child = daytona.create(childParams);
// The link network registers each sandbox under its name as a DNS alias
ExecuteResponse response = child.getProcess()
.executeCommand("curl http://" + parent.getName() + ":3000/");
}
}
}
```
```bash
# Create parent sandbox
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{}'
# Create linked child sandbox (replace PARENT_SANDBOX_ID with the id from the first response)
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"linkedSandbox": "PARENT_SANDBOX_ID",
"autoDeleteInterval": 0
}'
```
## Sandbox operations
Sandbox operations manage sandboxes after creation. They cover discovery, inspection, and updates to sandbox metadata and resources. Once a sandbox exists, you can find it among others in your organization, inspect its configuration, and update attributes such as metadata and allocated resources. A sandbox transitions between states such as started, stopped, paused, and archived as part of the [sandbox lifecycle](#sandbox-lifecycle).
### List sandboxes
List sandboxes.
```python
for sandbox in daytona.list():
print(sandbox.id)
```
```typescript
for await (const sandbox of daytona.list()) {
console.log(sandbox.id)
}
```
```ruby
daytona.list.each { |sandbox| puts sandbox.id }
```
```go
iter := client.List(ctx, nil)
for iter.Next() {
sandbox := iter.Value()
fmt.Println(sandbox.ID)
}
if err := iter.Err(); err != nil {
log.Fatal(err)
}
```
```java
for (Sandbox sandbox : daytona.list()) {
System.out.println(sandbox.getId());
}
```
```bash
daytona list [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Get sandbox
Get a sandbox by ID or name.
```python
sandbox = daytona.get("my-sandbox-id-or-name")
```
```typescript
const sandbox = await daytona.get('my-sandbox-id-or-name')
```
```ruby
sandbox = daytona.get('my-sandbox-id-or-name')
```
```go
sandbox, err := client.Get(ctx, "my-sandbox-id-or-name")
```
```java
Sandbox sandbox = daytona.get("my-sandbox-id-or-name");
```
```bash
daytona info [SANDBOX_ID] | [SANDBOX_NAME] [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}' \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Label sandboxes
Set sandbox labels.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Click
4. Enter the labels in key-value pairs
```python
sandbox.set_labels({
"team": "platform",
"env": "staging",
})
```
```typescript
await sandbox.setLabels({
team: 'platform',
env: 'staging',
})
```
```ruby
sandbox.labels = {
team: 'platform',
env: 'staging'
}
```
```go
err := sandbox.SetLabels(ctx, map[string]string{
"team": "platform",
"env": "staging",
})
```
```java
Map labels = new HashMap<>();
labels.put("team", "platform");
labels.put("env", "staging");
sandbox.setLabels(labels);
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/labels' \
--request PUT \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"labels": {
"team": "platform",
"env": "staging"
}
}'
```
### Resize sandboxes
Resizing updates the sandbox resource allocation (CPU, memory, and disk) for that sandbox. CPU and memory control compute capacity for running workloads, while disk controls the persistent filesystem capacity.
On a running sandbox, you can increase CPU and memory without interruption. To decrease CPU or memory, or to increase disk capacity, stop the sandbox first. Disk size can only be increased and cannot be decreased.
:::note
Sandbox resizing must be enabled for your organization. Contact [support@daytona.io](mailto:support@daytona.io).
:::
1. Choose the new **CPU**, **memory**, and **disk** values within your organization's limits
2. Ensure the sandbox is **stopped** if you need to decrease CPU or memory, or increase disk
3. **Resize** the sandbox with the new resource values
4. **Start** the sandbox
```python
from daytona import Resources
# Resize a started sandbox (CPU and memory can be increased)
sandbox.resize(Resources(cpu=2, memory=4))
# Resize a stopped sandbox (CPU and memory can change, disk can only increase)
sandbox.stop()
sandbox.resize(Resources(cpu=4, memory=8, disk=20))
sandbox.start()
```
```typescript
// Resize a started sandbox (CPU and memory can be increased)
await sandbox.resize({ cpu: 2, memory: 4 })
// Resize a stopped sandbox (CPU and memory can change, disk can only increase)
await sandbox.stop()
await sandbox.resize({ cpu: 4, memory: 8, disk: 20 })
await sandbox.start()
```
```ruby
# Resize a started sandbox (CPU and memory can be increased)
sandbox.resize(Daytona::Resources.new(cpu: 2, memory: 4))
# Resize a stopped sandbox (CPU and memory can change, disk can only increase)
sandbox.stop
sandbox.resize(Daytona::Resources.new(cpu: 4, memory: 8, disk: 20))
sandbox.start
```
```go
// Resize a started sandbox (CPU and memory can be increased)
err := sandbox.Resize(ctx, &types.Resources{CPU: 2, Memory: 4})
// Resize a stopped sandbox (CPU and memory can change, disk can only increase)
err = sandbox.Stop(ctx)
err = sandbox.Resize(ctx, &types.Resources{CPU: 4, Memory: 8, Disk: 20})
err = sandbox.Start(ctx)
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/resize' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"cpu": 2,
"memory": 4,
"disk": 20
}'
```
To verify CPU and memory limits inside the sandbox after resizing, read `cgroup` values directly. Tools such as `nproc`, `free`, `top`, `htop`, `/proc/cpuinfo`, and `/proc/meminfo` read host-level values and do not reflect sandbox resource limits.
```bash
cat /sys/fs/cgroup/cpu.max # "" (cores = quota / period)
cat /sys/fs/cgroup/memory.max # bytes
df -h / # disk
```
## Sandbox lifecycle
Every sandbox moves through a lifecycle. A sandbox can have several different states. Each state reflects the status of your sandbox. A sandbox transitions between states in response to user actions, or through [automated lifecycle management](#automated-lifecycle-management) based on inactivity intervals. Available lifecycle features depend on the sandbox class.
| **Lifecycle feature** | **Container** | **Linux VM** | **Windows** | **GPU** |
| ------------------------------------------------- | ------------- | ------------ | ----------- | ------- |
| Start sandboxes | ✓ | ✓ | ✓ | ✓ |
| Pause / resume sandboxes | ✗ | ✓ | ✓ | ✗ |
| Stop sandboxes | ✓ | ✓ | ✓ | ✓ |
| Archive sandboxes | ✓ | ✗ | ✗ | ✗ |
| Fork sandboxes | ✗ | ✓ | ✓ | ✗ |
| Snapshot from sandbox (filesystem only) | ✓ | ✓ | ✓ | ✓ |
| Snapshot from sandbox (filesystem + memory) | ✗ | ✓ | ✓ | ✗ |
| **State** | **Description** |
| ----------------- | ------------------------------------------------------------------------------------------- |
| Creating | The sandbox is provisioning and will be ready to use. |
| Pulling Snapshot | The sandbox is pulling a [**snapshot**](https://www.daytona.io/docs/en/snapshots.md) to provide a base environment. |
| Building Snapshot | The sandbox is building a [**snapshot**](https://www.daytona.io/docs/en/snapshots.md) to provide a base environment. |
| Pending Build | The sandbox build is pending and will start shortly. GPU sandboxes wait in this state until [GPU capacity](#gpu-capacity) is available. |
| Build Failed | The sandbox build failed and needs to be retried. |
| Starting | The sandbox is starting and will be ready to use. |
| Started | The sandbox has started and is ready to use. |
| Stopping | The sandbox is stopping and will no longer accept requests. |
| Stopped | The sandbox has stopped and is no longer running. Container sandboxes keep their filesystem on the runner. VM sandboxes offload filesystem state to nearby storage. |
| Pausing | The VM sandbox is pausing while its filesystem and memory state are preserved. |
| Paused | The VM sandbox is paused with filesystem and memory state preserved. State is offloaded to nearby storage. |
| Resuming | The VM sandbox is resuming from a paused state and will be ready to use. |
| Archiving | The container sandbox filesystem is being moved to object storage. |
| Archived | The container sandbox filesystem is stored in object storage. |
| Restoring | The sandbox is being restored and will be ready to use shortly. |
| Resizing | The sandbox is being resized to a new set of resources. |
| Snapshotting | The sandbox is creating a [**snapshot**](https://www.daytona.io/docs/en/snapshots.md) of its filesystem and memory. |
| Forking | The sandbox is being forked into a new independent sandbox. |
| Deleting | The sandbox is deleting and will be removed. |
| Deleted | The sandbox has been deleted and no longer exists. |
| Error | The sandbox is in an error state and needs to be recovered. |
| Unknown | The default sandbox state before it is created. |
A sandbox can transition between states in response to various actions. The following table lists the initial state, target state, and trigger for the transition.
| **Initial state** | **Target state** | **Trigger** |
| ----------------- | ----------------- | --------------------------------------------------------------------------------- |
| Unknown | Pulling Snapshot | The base snapshot is being pulled to provide the sandbox environment. |
| Unknown | Building Snapshot | The sandbox uses a declarative image build, which begins building. |
| Pending Build | Building Snapshot | The queued image build starts. |
| Building Snapshot | Build Failed | The image build fails or times out. |
| Pulling Snapshot | Creating | The snapshot is available and the sandbox container is created. |
| Building Snapshot | Creating | The snapshot finishes building and the sandbox container is created. |
| Creating | Started | The sandbox container finishes initializing and is running. |
| Stopped | Starting | A start is requested and the sandbox boots. |
| Stopped | Restoring | A start is requested and the sandbox is restored from a backup. |
| Archived | Restoring | A start is requested and the archived filesystem is restored from object storage. |
| Restoring | Started | The restore completes and the sandbox is running. |
| Starting | Started | The sandbox is running and ready to accept requests. |
| Started | Stopping | A stop is requested, or the auto-stop interval is exceeded. |
| Stopping | Stopped | The sandbox process exits and its memory state is cleared. |
| Started | Pausing | A pause is requested, or the auto-pause interval is exceeded. |
| Pausing | Paused | The filesystem and memory state are preserved. |
| Paused | Resuming | A start is requested on a paused sandbox. |
| Paused | Stopping | A stop is requested on a paused sandbox. |
| Resuming | Started | The sandbox resumes from memory and is running. |
| Stopped | Archiving | An archive is requested, or the auto-archive interval is exceeded. |
| Archiving | Archived | The backup completes and the filesystem is moved to object storage. |
| Started | Resizing | CPU or memory is increased on a running sandbox. |
| Stopped | Resizing | Resources are changed on a stopped sandbox. |
| Resizing | Started | The running sandbox returns to service after resizing. |
| Resizing | Stopped | The stopped sandbox completes resizing. |
| Started | Snapshotting | A snapshot of the filesystem and memory is created. |
| Stopped | Snapshotting | A snapshot of the filesystem is created. |
| Snapshotting | Started | The snapshot completes and the sandbox returns to service. |
| Snapshotting | Stopped | The snapshot completes and the sandbox remains stopped. |
| Started | Forking | The sandbox is forked into a new independent sandbox. |
| Forking | Started | The fork completes and the sandbox returns to service. |
| Started | Deleting | A delete is requested, or the auto-delete interval is exceeded. |
| Stopped | Deleting | A delete is requested. |
| Archived | Deleted | An archived sandbox is deleted directly without being restored. |
| Deleting | Deleted | The sandbox is removed and its resources are released. |
| Started | Error | An operation fails or times out. |
| Error | Restoring | A recover is requested for a recoverable error and the sandbox is restored. |
| Error | Archiving | An errored sandbox with a completed backup is archived to preserve its state. |
### Start sandboxes
Start a sandbox.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click the start icon next to the sandbox you want to start
```python
sandbox.start()
```
```typescript
await sandbox.start()
```
```ruby
sandbox.start
```
```go
sandbox.Start(ctx)
```
```java
sandbox.start();
```
```bash
daytona start [SANDBOX_ID] | [SANDBOX_NAME] [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/start' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Pause / resume sandboxes
Pause and resume a sandbox.
Pause is not supported for container sandboxes. The filesystem can be preserved on stop, but memory state is not. Use [**stop**](#stop-sandboxes) to shut down a container sandbox when it is not in use.
The filesystem and memory state are preserved, and CPU is no longer consumed.
1. Ensure the Linux VM sandbox is **started**
2. **Pause** the Linux VM sandbox
3. Wait for the Linux VM sandbox to reach the **paused** state
4. **Resume** (start) the Linux VM sandbox again when you need to resume it
```python
sandbox.pause()
```
```typescript
await sandbox.pause()
```
```ruby
sandbox.pause
```
```go
sandbox.Pause(ctx)
```
```java
sandbox.pause();
```
```bash
daytona pause [SANDBOX_ID] | [SANDBOX_NAME]
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/pause' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
The filesystem and memory state are preserved, and CPU is no longer consumed.
1. Ensure the Windows sandbox is **started**
2. **Pause** the Windows sandbox
3. Wait for the Windows sandbox to reach the **paused** state
4. **Resume** (start) the Windows sandbox again when you need to resume it
```python
sandbox.pause()
```
```typescript
await sandbox.pause()
```
```ruby
sandbox.pause
```
```go
sandbox.Pause(ctx)
```
```java
sandbox.pause();
```
```bash
daytona pause [SANDBOX_ID] | [SANDBOX_NAME]
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/pause' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Stop sandboxes
Stop a sandbox. The sandbox moves to the **stopped** state when shutdown completes. While a stop is in progress, the sandbox is in the **stopping** state and does not accept new requests.
Stopping terminates the running container. The filesystem is preserved, but memory state is not. Container sandboxes do not support pause; stop is the way to shut down a container sandbox when it is not in use.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click the stop icon (**⏹**) next to the sandbox you want to stop
```python
sandbox.stop()
```
```typescript
await sandbox.stop()
```
```ruby
sandbox.stop
```
```go
sandbox.Stop(ctx)
```
```java
sandbox.stop();
```
```bash
daytona stop [SANDBOX_ID] | [SANDBOX_NAME] [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/stop' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
Stopping shuts down the virtual machine while preserving the filesystem. Memory state is cleared. To preserve running process state without consuming CPU, use [**pause / resume**](#pause--resume-sandboxes).
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click the stop icon (**⏹**) next to the sandbox you want to stop
```python
sandbox.stop()
```
```typescript
await sandbox.stop()
```
```ruby
sandbox.stop
```
```go
sandbox.Stop(ctx)
```
```java
sandbox.stop();
```
```bash
daytona stop [SANDBOX_ID] | [SANDBOX_NAME] [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/stop' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
Stopping shuts down the virtual machine while preserving the filesystem. Memory state is cleared. To preserve running process state without consuming CPU, use [**pause / resume**](#pause--resume-sandboxes).
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click the stop icon (**⏹**) next to the sandbox you want to stop
```python
sandbox.stop()
```
```typescript
await sandbox.stop()
```
```ruby
sandbox.stop
```
```go
sandbox.Stop(ctx)
```
```java
sandbox.stop();
```
```bash
daytona stop [SANDBOX_ID] | [SANDBOX_NAME] [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/stop' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
:::note[Force stop]
If you need a faster shutdown, use force stop (`force=true` / `--force`) to terminate the sandbox immediately. Force stop is ungraceful and should be used when quick termination is more important than process cleanup. Avoid force stop for normal shutdowns where the process should flush buffers, write final state, or run cleanup.
:::
### Archive sandboxes
Archive a sandbox.
Archive moves a stopped sandbox's filesystem to object storage and frees disk quota.
1. Ensure the sandbox is **stopped**
2. **Archive** the sandbox
3. Wait for the sandbox to reach the **archived** state
4. **Start** the sandbox again when you need to use it
```python
sandbox.archive()
```
```typescript
await sandbox.archive()
```
```ruby
sandbox.archive
```
```go
sandbox.Archive(ctx)
```
```bash
daytona archive [SANDBOX_ID] | [SANDBOX_NAME] [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/archive' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
Archive is not supported for Linux VM sandboxes. Stopping a Linux VM sandbox already offloads filesystem state and releases disk quota, so a separate archive step is not needed.
Archive is not supported for Windows sandboxes. Stopping a Windows sandbox already offloads filesystem state and releases disk quota, so a separate archive step is not needed.
### Delete sandboxes
Delete a sandbox.
:::note
By default delete is fire-and-forget: it returns as soon as the API accepts the deletion request, without waiting for the sandbox to be destroyed. Pass the `wait` flag to block until the sandbox reaches the destroyed state.
:::
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click next to the sandbox you want to delete.
```python
sandbox.delete()
# Block until the sandbox is destroyed
sandbox.delete(timeout=60, wait=True)
```
```typescript
await sandbox.delete()
// Block until the sandbox is destroyed
await sandbox.delete(60, true)
```
```ruby
sandbox.delete
# Block until the sandbox is destroyed
sandbox.delete(60, wait: true)
```
```go
err = sandbox.Delete(ctx)
// Block until the sandbox is destroyed
err = sandbox.DeleteAndWait(ctx, 60*time.Second)
```
```java
sandbox.delete();
// Block until the sandbox is destroyed
sandbox.delete(60, true);
```
```bash
daytona delete [SANDBOX_ID] | [SANDBOX_NAME] [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}' \
--request DELETE \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Recover sandboxes
Recover a sandbox.
1. Ensure the sandbox is in **error** state
2. Check that the sandbox is **recoverable**
3. Resolve any underlying issue that requires user intervention
4. **Recover** the sandbox and wait for it to be ready
```python
# Check if the sandbox is recoverable
if sandbox.recoverable:
sandbox.recover()
```
```python
sandbox.recover()
```
```typescript
// Check if the sandbox is recoverable
if (sandbox.recoverable) {
await sandbox.recover()
}
```
```typescript
await sandbox.recover()
```
```ruby
# Check if the sandbox is in an error state before recovering
if sandbox.state == 'error'
sandbox.recover
end
```
```ruby
sandbox.recover
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/recover' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/recover' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Fork sandboxes
Fork a sandbox.
Forking is not supported for container sandboxes. Use [**create snapshot from sandbox**](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox) to capture filesystem state, then create a new sandbox from that snapshot.
Forking creates a duplicate of a Linux VM sandbox's filesystem and memory state in a new sandbox. The forked sandbox is fully independent: it can be started, stopped, and deleted without affecting the original.
Daytona tracks the parent-child relationship in a fork tree, so you can trace a fork's lineage back to the sandbox it was created from. You can fork a fork to build branches. The parent sandbox cannot be deleted while it has active fork children.
A fork of a [KVM sandbox](#nested-virtualization) inherits the `kvm` setting. Forking a KVM sandbox returns a `403` error if KVM sandboxes are not enabled for your organization.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click the three-dot menu (**⋮**) next to the started Linux VM sandbox you want to fork
3. Select
```python
# Fork sandbox through the Sandbox instance
forked = sandbox.fork(name="my-forked-sandbox")
```
```typescript
// Fork sandbox through the Sandbox instance
const forkedSandbox = await sandbox.fork({ name: "my-forked-sandbox" });
// Or use the Daytona helper method
const helperForkedSandbox = await daytona.fork(sandbox, { name: "my-forked-sandbox" });
```
```ruby
# Fork sandbox through the Sandbox instance
forkedSandbox = sandbox.fork(name: "my-forked-sandbox")
```
```go
// Fork sandbox through the Sandbox instance
name := "my-forked-sandbox"
forkedSandbox, err := sandbox.Fork(ctx, &name)
if err != nil {
return err
}
```
```java
// Fork sandbox through the Sandbox instance
Sandbox forkedSandbox = sandbox.fork("my-forked-sandbox", 60);
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/fork' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-forked-sandbox"
}'
```
Query fork relationships:
```bash
# List direct fork children of a sandbox
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/forks' \
--header 'Authorization: Bearer YOUR_API_KEY'
# Get the parent sandbox of a fork
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/parent' \
--header 'Authorization: Bearer YOUR_API_KEY'
# Get the full ancestor chain (parent, grandparent, and so on)
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/ancestors' \
--header 'Authorization: Bearer YOUR_API_KEY'
```
Forking creates a duplicate of a Windows sandbox's filesystem and memory state in a new sandbox. The forked sandbox is fully independent: it can be started, stopped, and deleted without affecting the original.
Daytona tracks the parent-child relationship in a fork tree, so you can trace a fork's lineage back to the sandbox it was created from. You can fork a fork to build branches. The parent sandbox cannot be deleted while it has active fork children.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click the three-dot menu (**⋮**) next to the started Windows sandbox you want to fork
3. Select
```python
# Fork sandbox through the Sandbox instance
forked = sandbox.fork(name="my-forked-sandbox")
```
```typescript
// Fork sandbox through the Sandbox instance
const forkedSandbox = await sandbox.fork({ name: "my-forked-sandbox" });
// Or use the Daytona helper method
const helperForkedSandbox = await daytona.fork(sandbox, { name: "my-forked-sandbox" });
```
```ruby
# Fork sandbox through the Sandbox instance
forkedSandbox = sandbox.fork(name: "my-forked-sandbox")
```
```go
// Fork sandbox through the Sandbox instance
name := "my-forked-sandbox"
forkedSandbox, err := sandbox.Fork(ctx, &name)
if err != nil {
return err
}
```
```java
// Fork sandbox through the Sandbox instance
Sandbox forkedSandbox = sandbox.fork("my-forked-sandbox", 60);
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/fork' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-forked-sandbox"
}'
```
Query fork relationships:
```bash
# List direct fork children of a sandbox
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/forks' \
--header 'Authorization: Bearer YOUR_API_KEY'
# Get the parent sandbox of a fork
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/parent' \
--header 'Authorization: Bearer YOUR_API_KEY'
# Get the full ancestor chain (parent, grandparent, and so on)
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/ancestors' \
--header 'Authorization: Bearer YOUR_API_KEY'
```
## Automated lifecycle management
Sandboxes can be managed automatically based on user-defined deadlines. Inactivity and stopped-time intervals stop, pause, archive, or delete a sandbox when it is idle. Wall-clock TTL destroys a sandbox after a fixed deadline regardless of state.
- **[Auto-stop interval](#auto-stop-interval)**: stop a sandbox after a specified period of inactivity
- **[Auto-pause interval](#auto-pause-interval)**: pause a VM sandbox after a specified period of inactivity
- **[Auto-archive interval](#auto-archive-interval)**: archive a sandbox after a specified period of inactivity
- **[Auto-delete interval](#auto-delete-interval)**: delete a sandbox after a specified period of inactivity
- **[Wall-clock TTL](#wall-clock-ttl)**: destroy a sandbox after a fixed wall-clock deadline, regardless of state
- **[Update sandbox last activity](#update-sandbox-last-activity)**: signal activity to reset the inactivity timer
- **[Running indefinitely](#running-indefinitely)**: run a sandbox indefinitely
### Auto-stop sandboxes
The auto-stop interval sets the amount of time after which a running sandbox is automatically stopped. The auto-stop triggers even if there are internal processes running in the sandbox.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Set **`auto-stop`** interval to the desired value in minutes
- **`0`**: disables the auto-stop functionality, allowing the sandbox to run indefinitely
- if not set, the default interval of 15 minutes is used
4. Click
```python
from daytona import CreateSandboxFromSnapshotParams
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
snapshot="my-snapshot",
# Disables the auto-stop feature - default is 15 minutes
auto_stop_interval=0,
))
```
```typescript
const sandbox = await daytona.create({
snapshot: 'my-snapshot',
// Disables the auto-stop feature - default is 15 minutes
autoStopInterval: 0,
})
```
```ruby
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'my-snapshot',
# Disables the auto-stop feature - default is 15 minutes
auto_stop_interval: 0
)
)
```
```go
// Create a sandbox with auto-stop disabled
autoStopInterval := 0
params := types.SnapshotParams{
Snapshot: "my-snapshot",
SandboxBaseParams: types.SandboxBaseParams{
AutoStopInterval: &autoStopInterval,
},
}
sandbox, err := client.Create(ctx, params)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my-snapshot");
// Disables the auto-stop feature - default is 15 minutes
params.setAutoStopInterval(0);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/autostop/{interval}' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
The system differentiates between "internal processes" and "active user interaction". Merely having a script or background task running is not sufficient to keep the sandbox alive.
##### What resets the timer
The inactivity timer resets only for specific external interactions:
- Updates to [sandbox lifecycle states](#sandbox-lifecycle)
- Network requests through [sandbox previews](https://www.daytona.io/docs/en/preview.md)
- Active [SSH connections](https://www.daytona.io/docs/en/ssh-access.md)
- API requests to the [Daytona Toolbox SDK](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox)
##### What does not reset the timer
The following do not reset the timer:
- SDK requests that are not toolbox actions
- Background scripts (e.g., `npm run dev` run as a fire-and-forget command)
- Long-running tasks without external interaction
- Processes that don't involve active monitoring
If you run a long-running task like LLM inference that takes more than 15 minutes to complete without any external interaction, the sandbox may auto-stop mid-process because the process itself doesn't count as "activity", therefore the timer is not reset.
### Auto-pause sandboxes
The auto-pause interval sets the amount of time after which an idle VM sandbox is automatically [paused](#pause--resume-sandboxes). Auto-pause applies only to [VM sandboxes](#vm-sandboxes) and is mutually exclusive with the [auto-stop interval](#auto-stop-interval): at most one of the two intervals may be non-zero. Ephemeral sandboxes cannot have auto-pause enabled.
The interval is set in minutes:
- **`0`**: disables the auto-pause functionality
- if neither auto-pause nor auto-stop is set, non-ephemeral sandbox classes that support pausing default to an auto-pause interval of 60 minutes with auto-stop disabled
The sandbox pauses after no new events occur for the specified interval. Events include sandbox state changes and interactions with the sandbox through the SDK. Interactions through [sandbox previews](https://www.daytona.io/docs/en/preview.md) do not reset the timer.
Auto-pause is not supported for container sandboxes. Use [**auto-stop**](#auto-stop-interval) to stop a container sandbox after a period of inactivity.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Select a Linux VM snapshot
4. Set **`auto-pause`** interval to the desired value in minutes
- **`0`**: disables the auto-pause functionality
- if neither auto-pause nor auto-stop is set, the default interval of 60 minutes is used with auto-stop disabled
5. Click
```python
from daytona import CreateSandboxFromSnapshotParams
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
snapshot="daytona-vm-small",
# Auto-pause after 1 hour of inactivity
auto_pause_interval=60,
))
# Update the auto-pause interval on an existing sandbox
sandbox.set_auto_pause_interval(60)
# Disable auto-pause
sandbox.set_auto_pause_interval(0)
```
```typescript
const sandbox = await daytona.create({
snapshot: 'daytona-vm-small',
// Auto-pause after 1 hour of inactivity
autoPauseInterval: 60,
})
// Update the auto-pause interval on an existing sandbox
await sandbox.setAutoPauseInterval(60)
// Disable auto-pause
await sandbox.setAutoPauseInterval(0)
```
```ruby
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'daytona-vm-small',
# Auto-pause after 1 hour of inactivity
auto_pause_interval: 60
)
)
# Update the auto-pause interval on an existing sandbox
sandbox.auto_pause_interval = 60
# Disable auto-pause
sandbox.auto_pause_interval = 0
```
```go
// Auto-pause after 1 hour of inactivity
autoPauseInterval := 60
params := types.SnapshotParams{
Snapshot: "daytona-vm-small",
SandboxBaseParams: types.SandboxBaseParams{
AutoPauseInterval: &autoPauseInterval,
},
}
sandbox, err := client.Create(ctx, params)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("daytona-vm-small");
// Auto-pause after 1 hour of inactivity
params.setAutoPauseInterval(60);
Sandbox sandbox = daytona.create(params);
// Update the auto-pause interval on an existing sandbox
sandbox.setAutoPauseInterval(60);
// Disable auto-pause
sandbox.setAutoPauseInterval(0);
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/autopause/{interval}' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Select a Windows snapshot
4. Set **`auto-pause`** interval to the desired value in minutes
- **`0`**: disables the auto-pause functionality
- if neither auto-pause nor auto-stop is set, the default interval of 60 minutes is used with auto-stop disabled
5. Click
```python
from daytona import CreateSandboxFromSnapshotParams
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
snapshot="windows-small",
# Auto-pause after 1 hour of inactivity
auto_pause_interval=60,
))
# Update the auto-pause interval on an existing sandbox
sandbox.set_auto_pause_interval(60)
# Disable auto-pause
sandbox.set_auto_pause_interval(0)
```
```typescript
const sandbox = await daytona.create({
snapshot: 'windows-small',
// Auto-pause after 1 hour of inactivity
autoPauseInterval: 60,
})
// Update the auto-pause interval on an existing sandbox
await sandbox.setAutoPauseInterval(60)
// Disable auto-pause
await sandbox.setAutoPauseInterval(0)
```
```ruby
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'windows-small',
# Auto-pause after 1 hour of inactivity
auto_pause_interval: 60
)
)
# Update the auto-pause interval on an existing sandbox
sandbox.auto_pause_interval = 60
# Disable auto-pause
sandbox.auto_pause_interval = 0
```
```go
// Auto-pause after 1 hour of inactivity
autoPauseInterval := 60
params := types.SnapshotParams{
Snapshot: "windows-small",
SandboxBaseParams: types.SandboxBaseParams{
AutoPauseInterval: &autoPauseInterval,
},
}
sandbox, err := client.Create(ctx, params)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("windows-small");
// Auto-pause after 1 hour of inactivity
params.setAutoPauseInterval(60);
Sandbox sandbox = daytona.create(params);
// Update the auto-pause interval on an existing sandbox
sandbox.setAutoPauseInterval(60);
// Disable auto-pause
sandbox.setAutoPauseInterval(0);
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/autopause/{interval}' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
Auto-pause is not supported for GPU sandboxes. GPU sandboxes are ephemeral and cannot have auto-pause enabled.
### Auto-archive sandboxes
The auto-archive interval sets the amount of time after which a continuously stopped sandbox is automatically archived. Auto-archive applies only to container sandboxes. VM sandboxes are excluded.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Set **`auto-archive`** interval to the desired value in minutes
- **`0`**: the maximum interval of 30 days is used
- if not set, the default interval of 7 days is used
4. Click
```python
from daytona import CreateSandboxFromSnapshotParams
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
snapshot="my-snapshot",
# Auto-archive after a sandbox has been stopped for 1 hour
auto_archive_interval=60,
))
```
```typescript
const sandbox = await daytona.create({
snapshot: 'my-snapshot',
// Auto-archive after a sandbox has been stopped for 1 hour
autoArchiveInterval: 60,
})
```
```ruby
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'my-snapshot',
# Auto-archive after a sandbox has been stopped for 1 hour
auto_archive_interval: 60
)
)
```
```go
// Create a sandbox with auto-archive after 1 hour
autoArchiveInterval := 60
params := types.SnapshotParams{
Snapshot: "my-snapshot",
SandboxBaseParams: types.SandboxBaseParams{
AutoArchiveInterval: &autoArchiveInterval,
},
}
sandbox, err := client.Create(ctx, params)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my-snapshot");
// Auto-archive after a sandbox has been stopped for 1 hour
params.setAutoArchiveInterval(60);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/autoarchive/{interval}' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Auto-delete sandboxes
The auto-delete interval sets the amount of time after which a continuously stopped sandbox is automatically deleted.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Set **`auto-delete`** to the desired value in minutes
- `-1`: disables the auto-delete functionality
- `0`: the sandbox is deleted immediately after it is stopped
- if not set, the sandbox is not deleted automatically
4. Click
```python
from daytona import CreateSandboxFromSnapshotParams
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
snapshot="my-snapshot",
# Auto-delete after a sandbox has been stopped for 1 hour
auto_delete_interval=60,
))
# Delete the sandbox immediately after it has been stopped
sandbox.set_auto_delete_interval(0)
# Disable auto-deletion
sandbox.set_auto_delete_interval(-1)
```
```typescript
const sandbox = await daytona.create({
snapshot: 'my-snapshot',
// Auto-delete after a sandbox has been stopped for 1 hour
autoDeleteInterval: 60,
})
// Delete the sandbox immediately after it has been stopped
await sandbox.setAutoDeleteInterval(0)
// Disable auto-deletion
await sandbox.setAutoDeleteInterval(-1)
```
```ruby
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'my-snapshot',
# Auto-delete after a sandbox has been stopped for 1 hour
auto_delete_interval: 60
)
)
# Delete the sandbox immediately after it has been stopped
sandbox.auto_delete_interval = 0
# Disable auto-deletion
sandbox.auto_delete_interval = -1
```
```go
// Create a sandbox with auto-delete after 1 hour
autoDeleteInterval := 60
params := types.SnapshotParams{
Snapshot: "my-snapshot",
SandboxBaseParams: types.SandboxBaseParams{
AutoDeleteInterval: &autoDeleteInterval,
},
}
sandbox, err := client.Create(ctx, params)
// Delete the sandbox immediately after it has been stopped
zeroInterval := 0
err = sandbox.SetAutoDeleteInterval(ctx, &zeroInterval)
// Disable auto-deletion
disableInterval := -1
err = sandbox.SetAutoDeleteInterval(ctx, &disableInterval)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my-snapshot");
// Auto-delete after a sandbox has been stopped for 1 hour
params.setAutoDeleteInterval(60);
Sandbox sandbox = daytona.create(params);
// Delete the sandbox immediately after it has been stopped
sandbox.setAutoDeleteInterval(0);
// Disable auto-deletion
sandbox.setAutoDeleteInterval(-1);
}
}
}
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/autodelete/{interval}' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Wall-clock TTL
The wall-clock TTL (time-to-live) sets a hard upper bound on how long a sandbox may exist. Unlike the [auto-delete interval](#auto-delete-interval), which counts time only while the sandbox is stopped, TTL runs as wall-clock time from creation (or from the moment you last set it) and destroys the sandbox in any state: started, stopped, paused, or archived.
Set `ttl_minutes` when creating a sandbox, or update it later. The value is in minutes:
- **`0`**: disables the TTL
- if not set, the sandbox has no TTL deadline
Calling `set_ttl` after creation resets the deadline from the current moment. Use wall-clock TTL for agent sessions, CI jobs, and any sandbox that must not outlive a fixed deadline.
```python
from daytona import CreateSandboxFromSnapshotParams
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
snapshot="my-snapshot",
# Destroy the sandbox 2 hours after creation, regardless of state
ttl_minutes=120,
))
# Reset the deadline to 1 hour from now
sandbox.set_ttl(60)
# Disable the TTL
sandbox.set_ttl(0)
```
```typescript
const sandbox = await daytona.create({
snapshot: 'my-snapshot',
// Destroy the sandbox 2 hours after creation, regardless of state
ttlMinutes: 120,
})
// Reset the deadline to 1 hour from now
await sandbox.setTtl(60)
// Disable the TTL
await sandbox.setTtl(0)
```
```ruby
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'my-snapshot',
# Destroy the sandbox 2 hours after creation, regardless of state
ttl_minutes: 120
)
)
# Reset the deadline to 1 hour from now
sandbox.ttl_minutes = 60
# Disable the TTL
sandbox.ttl_minutes = 0
```
```go
// Destroy the sandbox 2 hours after creation, regardless of state
ttlMinutes := 120
params := types.SnapshotParams{
Snapshot: "my-snapshot",
SandboxBaseParams: types.SandboxBaseParams{
TtlMinutes: &ttlMinutes,
},
}
sandbox, err := client.Create(ctx, params)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my-snapshot");
// Destroy the sandbox 2 hours after creation, regardless of state
params.setTtlMinutes(120);
Sandbox sandbox = daytona.create(params);
// Reset the deadline to 1 hour from now
sandbox.setTtl(60);
// Disable the TTL
sandbox.setTtl(0);
}
}
}
```
```bash
# Destroy the sandbox 2 hours after creation, regardless of state
daytona create --ttl 120
```
```bash
# Create with a 2-hour wall-clock TTL
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "my-snapshot",
"ttlMinutes": 120
}'
# Reset the deadline to 1 hour from now
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/ttl/60' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
# Disable the TTL
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/ttl/0' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Update sandbox last activity
Update a sandbox's last activity timestamp.
This updates the sandbox's recorded activity time without changing its runtime state. It is useful when your workflow is driven by external systems or background orchestration that may not reset inactivity tracking.
```python
sandbox.refresh_activity()
```
```typescript
await sandbox.refreshActivity()
```
```ruby
sandbox.refresh_activity
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxId}/last-activity' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
### Running indefinitely
Run sandboxes indefinitely.
By default, Daytona sandboxes auto-stop after 15 minutes of inactivity. To keep a sandbox running without interruption from inactivity, set the auto-stop interval to `0` when creating a new sandbox. Disabling auto-stop does not disable [wall-clock TTL](#wall-clock-ttl): if `ttl_minutes` is set, the sandbox is still destroyed when that deadline elapses.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Set **`auto-stop`** to **`0`**
4. Click
```python
from daytona import CreateSandboxFromSnapshotParams
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
snapshot="my_awesome_snapshot",
# Disables the auto-stop feature - default is 15 minutes
auto_stop_interval=0,
))
```
```typescript
const sandbox = await daytona.create({
snapshot: 'my_awesome_snapshot',
// Disables the auto-stop feature - default is 15 minutes
autoStopInterval: 0,
})
```
```ruby
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'my_awesome_snapshot',
# Disables the auto-stop feature - default is 15 minutes
auto_stop_interval: 0
)
)
```
```go
// Disables the auto-stop feature - default is 15 minutes
autoStopInterval := 0
params := types.SnapshotParams{
Snapshot: "my_awesome_snapshot",
SandboxBaseParams: types.SandboxBaseParams{
AutoStopInterval: &autoStopInterval,
},
}
sandbox, err := client.Create(ctx, params)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my_awesome_snapshot");
// Disables the auto-stop feature - default is 15 minutes
params.setAutoStopInterval(0);
Sandbox sandbox = daytona.create(params);
}
}
}
```
# Isolation
Daytona sandboxes are isolated by default. Code running in a sandbox cannot read another sandbox's filesystem or memory, is not on a shared network with other sandboxes, and its credentials and API access are scoped to its own organization.
Isolation operates at three boundaries:
| **Boundary** | **What is separated** | **Mechanisms** |
| ------------ | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Runtime | Processes, filesystem, memory, and devices of each sandbox | • [**Sandbox classes**](https://www.daytona.io/docs/en/sandboxes.md) • [**Reserved resources**](https://www.daytona.io/docs/en/sandboxes.md#resources) |
| Network | Traffic entering (ingress) and leaving (egress) each sandbox | • [**Network limits**](https://www.daytona.io/docs/en/network-limits.md) • [**Preview authentication**](https://www.daytona.io/docs/en/preview.md) • [**Link networks**](https://www.daytona.io/docs/en/sandboxes.md#linked-sandboxes) |
| Organization | Access to sandboxes, data, and credentials | • [**Organizations**](https://www.daytona.io/docs/en/organizations.md) • [**API key permissions**](https://www.daytona.io/docs/en/api-keys.md#permissions--scopes) • [**Secrets**](https://www.daytona.io/docs/en/secrets.md) |
## Runtime isolation
Runtime isolation separates what runs inside one sandbox from the runner it executes on and from every other sandbox. Each sandbox runs as an isolated instance with its own processes, network, filesystem mounts, and inter-process communication: see [architecture](https://www.daytona.io/docs/en/architecture.md#sandbox-runners) for details.
Resources are part of the runtime boundary. Each sandbox reserves its own **vCPU**, **memory**, and **disk**, enforced as hard limits, so one sandbox cannot consume the resources of another regardless of what its code does. Sandbox classes differ in the kind of boundary they provide:
| **Sandbox class** | **Runtime boundary** |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Container | Isolated container with dedicated namespaces and enforced resource limits. Code runs as root inside the sandbox without affecting the runner. |
| VM sandboxes (Linux VM and Windows) | Full virtual machine with its own kernel. The hardware virtualization boundary enables VM-only capabilities: [**pause / resume**](https://www.daytona.io/docs/en/sandboxes.md#pause--resume-sandboxes), [**fork**](https://www.daytona.io/docs/en/sandboxes.md#fork-sandboxes), and [**hot snapshots**](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox). |
| GPU | Isolated container with exclusive GPU allocation: assigned GPU devices belong to one sandbox at a time and are never shared. |
Resource limits are visible inside the sandbox through `cgroup` values. Tools such as `nproc` and `free` read host-level values and do not reflect the sandbox's own limits:
```bash
cat /sys/fs/cgroup/cpu.max # "" (cores = quota / period)
cat /sys/fs/cgroup/memory.max # bytes
df -h / # disk
```
## Network isolation
Network isolation controls traffic in each direction separately. Outbound and inbound access are configured per sandbox; sandbox-to-sandbox networking is off unless sandboxes are explicitly linked.
| **Direction** | **Default** | **Controls** |
| ------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Sandbox to internet | Open on Tier 3 and above; restricted on Tier 1 and 2 | [**Network limits**](https://www.daytona.io/docs/en/network-limits.md): block all, CIDR allow list, domain allow list, or [**outbound proxy**](https://www.daytona.io/docs/en/network-limits.md#outbound-proxy) |
| Internet to sandbox | Authenticated preview URLs and SSH access | [**Preview tokens and signed URLs**](https://www.daytona.io/docs/en/preview.md) and [**SSH tokens**](https://www.daytona.io/docs/en/ssh-access.md); the **`public`** flag opts previews out of authentication |
| Sandbox to sandbox | No shared network | [**Linked sandboxes**](https://www.daytona.io/docs/en/sandboxes.md#linked-sandboxes) join a parent and its children into a link network |
**Outbound** traffic passes a per-sandbox firewall. [Tier-based restrictions](https://www.daytona.io/docs/en/network-limits.md#tier-based-network-restrictions) apply automatically, and each sandbox can be locked down further with one of three mutually exclusive settings: block all traffic, allow specific CIDR ranges, or allow specific domains. Under the default tier policy, [essential services](https://www.daytona.io/docs/en/network-limits.md#essential-services) such as package registries stay reachable on all tiers. A sandbox-level allow list or block-all setting on Tier 3 or Tier 4 is strict: essential services do not bypass it.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
# Block all outbound traffic
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
network_block_all=True,
))
# Or allow specific domains only
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
domain_allow_list="example.com,*.daytona.io",
))
# Or allow specific CIDR ranges only
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
network_allow_list="208.80.154.232/32,192.168.1.0/24",
))
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
// Block all outbound traffic
const blocked = await daytona.create({
networkBlockAll: true,
})
// Or allow specific domains only
const domainRestricted = await daytona.create({
domainAllowList: 'example.com,*.daytona.io',
})
// Or allow specific CIDR ranges only
const cidrRestricted = await daytona.create({
networkAllowList: '208.80.154.232/32,192.168.1.0/24',
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
# Block all outbound traffic
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
network_block_all: true
)
)
# Or allow specific domains only
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
domain_allow_list: 'example.com,*.daytona.io'
)
)
# Or allow specific CIDR ranges only
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
network_allow_list: '208.80.154.232/32,192.168.1.0/24'
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
// Block all outbound traffic
_, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
NetworkBlockAll: true,
},
})
if err != nil {
// handle error
}
// Or allow specific domains only
domainAllowList := "example.com,*.daytona.io"
_, err = client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
DomainAllowList: &domainAllowList,
},
})
if err != nil {
// handle error
}
// Or allow specific CIDR ranges only
networkAllowList := "208.80.154.232/32,192.168.1.0/24"
_, err = client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
NetworkAllowList: &networkAllowList,
},
})
if err != nil {
// handle error
}
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
// Block all outbound traffic
CreateSandboxFromSnapshotParams blockedParams = new CreateSandboxFromSnapshotParams();
blockedParams.setNetworkBlockAll(true);
Sandbox blocked = daytona.create(blockedParams);
// Or allow specific domains only
CreateSandboxFromSnapshotParams domainParams = new CreateSandboxFromSnapshotParams();
domainParams.setDomainAllowList("example.com,*.daytona.io");
Sandbox domainRestricted = daytona.create(domainParams);
}
}
}
```
```bash
# Block all outbound traffic
daytona create --network-block-all
# Or allow specific CIDR ranges only
daytona create --network-allow-list '208.80.154.232/32,192.168.1.0/24'
```
```bash
# Block all outbound traffic
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"networkBlockAll": true
}'
# Or allow specific domains only
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"domainAllowList": "example.com,*.daytona.io"
}'
# Or allow specific CIDR ranges only
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"networkAllowList": "208.80.154.232/32,192.168.1.0/24"
}'
```
**Inbound** traffic reaches a sandbox through [preview URLs](https://www.daytona.io/docs/en/preview.md) or [SSH access](https://www.daytona.io/docs/en/ssh-access.md), and both paths are authenticated: preview URLs require a preview token or a signed URL unless the sandbox is explicitly made public, and SSH connections require an SSH access token.
```python
from daytona import Daytona
daytona = Daytona()
sandbox = daytona.create()
# Preview URLs require a token unless the sandbox is public
preview = sandbox.get_preview_link(3000)
print(preview.url) # https://3000-{sandboxId}.{proxy-domain}
print(preview.token) # sent via the x-daytona-preview-token header
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create()
// Preview URLs require a token unless the sandbox is public
const preview = await sandbox.getPreviewLink(3000)
console.log(preview.url) // https://3000-{sandboxId}.{proxy-domain}
console.log(preview.token) // sent via the x-daytona-preview-token header
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create
# Preview URLs require a token unless the sandbox is public
preview = sandbox.preview_url(3000)
puts preview.url # https://3000-{sandboxId}.{proxy-domain}
puts preview.token # sent via the x-daytona-preview-token header
```
```go
package main
import (
"context"
"fmt"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
sandbox, err := client.Create(ctx, types.SnapshotParams{})
if err != nil {
// handle error
}
// Preview URLs require a token unless the sandbox is public
preview, err := sandbox.GetPreviewLink(ctx, 3000)
if err != nil {
// handle error
}
fmt.Println(preview.URL) // https://3000-{sandboxId}.{proxy-domain}
fmt.Println(preview.Token) // sent via the x-daytona-preview-token header
}
```
```bash
# Create a public sandbox (preview URLs skip authentication)
daytona create --public
# Or get a signed preview URL for an existing sandbox
daytona preview-url --port 3000 --expires 3600
```
```bash
# Get a standard preview URL and token
curl 'https://app.daytona.io/api/sandbox/{sandboxId}/ports/3000/preview-url' \
--header 'Authorization: Bearer YOUR_API_KEY'
# Authenticate to the preview with the returned token
curl -H "x-daytona-preview-token: PREVIEW_TOKEN" \
"https://3000-{sandboxId}.{proxy-domain}"
```
**Between sandboxes**, there is no shared network. [Linked sandboxes](https://www.daytona.io/docs/en/sandboxes.md#linked-sandboxes) are the deliberate exception: children are scheduled onto the same runner as their parent and joined into a link network where each sandbox is reachable by name, while remaining isolated from every sandbox outside the group.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
parent = daytona.create()
# Only linked sandboxes share a network; everything else is isolated
child = daytona.create(CreateSandboxFromSnapshotParams(
linked_sandbox=parent.id,
ephemeral=True,
))
# Sandboxes on the link network are reachable by name
response = child.process.exec(f"curl http://{parent.name}:3000/")
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const parent = await daytona.create()
// Only linked sandboxes share a network; everything else is isolated
const child = await daytona.create({
linkedSandbox: parent.id,
ephemeral: true,
})
// Sandboxes on the link network are reachable by name
const response = await child.process.executeCommand(
`curl http://${parent.name}:3000/`
)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
parent = daytona.create
# Only linked sandboxes share a network; everything else is isolated
child = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
linked_sandbox: parent.id,
ephemeral: true
)
)
# The link network registers each sandbox under its name and ID as DNS aliases.
# The Ruby SDK does not expose the sandbox name, so address the parent by ID.
response = child.process.exec(command: "curl http://#{parent.id}:3000/")
```
```go
package main
import (
"context"
"fmt"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
parent, err := client.Create(ctx, types.SnapshotParams{})
if err != nil {
// handle error
}
// Only linked sandboxes share a network; everything else is isolated
child, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
LinkedSandbox: parent.ID,
Ephemeral: true,
},
})
if err != nil {
// handle error
}
// Sandboxes on the link network are reachable by name
response, err := child.Process.ExecuteCommand(
ctx, fmt.Sprintf("curl http://%s:3000/", parent.Name),
)
if err != nil {
// handle error
}
fmt.Println(response.Result)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Sandbox parent = daytona.create();
// Only linked sandboxes share a network; everything else is isolated
CreateSandboxFromSnapshotParams childParams = new CreateSandboxFromSnapshotParams();
childParams.setLinkedSandbox(parent.getId());
childParams.setAutoDeleteInterval(0); // linked sandboxes must be ephemeral
Sandbox child = daytona.create(childParams);
// Sandboxes on the link network are reachable by name
ExecuteResponse response = child.getProcess()
.executeCommand("curl http://" + parent.getName() + ":3000/");
}
}
}
```
```bash
# Create parent sandbox
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{}'
# Create linked child sandbox (replace PARENT_SANDBOX_ID)
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"linkedSandbox": "PARENT_SANDBOX_ID",
"autoDeleteInterval": 0
}'
```
## Organization isolation
Organization isolation separates tenants. Every sandbox, snapshot, and volume belongs to exactly one [organization](https://www.daytona.io/docs/en/organizations.md), and access control is enforced at that boundary: an API key from one organization cannot see or operate on another organization's resources.
Within an organization, access narrows further to the following mechanisms:
**[API key permissions](https://www.daytona.io/docs/en/api-keys.md#permissions--scopes)** scope what a key can do. A key issued with only `write:sandboxes` cannot delete snapshots or read volumes.
**[Managed API keys](https://www.daytona.io/docs/en/api-keys.md#managed-api-keys)** issue scoped child keys at runtime, so a multi-tenant application can hand each tenant a key limited to its own operations.
```bash
# A manager key issues a child key scoped to one tenant's operations;
# child key permissions must be a subset of the manager key's permissions
curl 'https://app.daytona.io/api/api-keys' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_MANAGER_API_KEY' \
--data '{
"name": "tenant-a-key",
"permissions": ["write:sandboxes", "delete:sandboxes"]
}'
```
**[Secrets](https://www.daytona.io/docs/en/secrets.md)** keep credentials out of sandboxes entirely. A sandbox holds an opaque placeholder; an outbound proxy substitutes the real value only for requests to the secret's allowed hosts. Code in the sandbox can use the credential but cannot read it or send it anywhere else.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
# The sandbox receives a placeholder, never the plaintext value
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
secrets={
"MY_API_KEY": "my-secret",
},
))
# Code uses the credential without being able to read it
sandbox.process.exec(
'curl -H "Authorization: Bearer $MY_API_KEY" https://api.example.com/v1/data'
)
```
**[Volumes](https://www.daytona.io/docs/en/volumes.md)** scope shared data with a `subpath`, so each sandbox mounts only its tenant's slice of a shared volume.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount
daytona = Daytona()
volume = daytona.volume.get("tenant-data", create=True)
# Each sandbox mounts only its tenant's slice of the shared volume
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
volumes=[VolumeMount(
volume_id=volume.id,
mount_path="/home/daytona/data",
subpath="tenants/tenant-a",
)],
))
```
# Persistence
Daytona sandboxes are persistent by default. Stopping a sandbox does not destroy it: the sandbox keeps its identity, its filesystem, and its configuration, and can be started again at any point with all files, installed packages, and repositories intact. A sandbox is only deleted when you delete it, mark it as [ephemeral](https://www.daytona.io/docs/en/sandboxes.md#ephemeral-sandboxes), or set an [auto-delete interval](https://www.daytona.io/docs/en/sandboxes.md#auto-delete-interval).
Persistence operates at three layers:
| **Layer** | **What is preserved** | **Mechanisms** |
| ---------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Filesystem | Files, installed packages, cloned repositories, build artifacts | • [**Stop / start**](https://www.daytona.io/docs/en/sandboxes.md#stop-sandboxes) • [**Archive**](https://www.daytona.io/docs/en/sandboxes.md#archive-sandboxes) • [**Cold snapshots**](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox) |
| Memory | Running processes, open connections, loaded application state | • [**Pause / resume**](https://www.daytona.io/docs/en/sandboxes.md#pause--resume-sandboxes) • [**Hot snapshots**](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox) • [**Fork**](https://www.daytona.io/docs/en/sandboxes.md#fork-sandboxes) |
| External storage | Data that outlives any single sandbox | • [**Volumes**](https://www.daytona.io/docs/en/volumes.md) • [**Mount external storage**](https://www.daytona.io/docs/en/mount-external-storage.md) |
The diagram demonstrates [VM (Linux VM and Windows) sandbox](https://www.daytona.io/docs/en/sandboxes.md#vm-sandboxes) states and transitions with [filesystem](#filesystem-persistence) and [memory](#memory-persistence) persistence. Container sandboxes preserve the filesystem across stop and start, and do not support pause/resume; memory is cleared on every stop.
## Filesystem persistence
Filesystem persistence keeps the contents of a sandbox's disk: files, installed packages, cloned repositories, and build artifacts. A sandbox with a preserved filesystem starts with its environment intact, with nothing to reinstall or rebuild.
Sandboxes preserve their filesystem across **stop and start** cycles, **archive**, and **snapshots created from a sandbox (cold snapshots)**. Sandbox classes differ in where and how the preserved filesystem is stored while the sandbox is stopped:
| **Sandbox class** | **Filesystem persistence** |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Container | Filesystem is preserved through [**stop / start**](https://www.daytona.io/docs/en/sandboxes.md#stop-sandboxes): it stays on the runner and counts against disk quota while stopped. [**Archive**](https://www.daytona.io/docs/en/sandboxes.md#archive-sandboxes) moves it to object storage and frees the quota; starting an archived sandbox restores it. Use [**cold snapshots**](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox) to capture the filesystem. |
| VM sandboxes (Linux VM and Windows) | Filesystem is preserved through [**stop / start**](https://www.daytona.io/docs/en/sandboxes.md#stop-sandboxes): stopping offloads it to nearby storage and releases disk quota, starting restores it. Archive is not needed. Use [**cold snapshots**](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox) to capture the filesystem. |
| GPU | Ephemeral by design: deleted on stop, filesystem is not preserved. Use [**volumes**](https://www.daytona.io/docs/en/volumes.md) to persist results. |
```python
from daytona import Daytona
daytona = Daytona()
sandbox = daytona.create()
# Write state to the filesystem
sandbox.fs.upload_file(b"intermediate results", "results.txt")
# Stop clears memory; the filesystem is preserved on the runner
sandbox.stop()
# Start returns the filesystem exactly as it was
sandbox.start()
response = sandbox.process.exec("cat results.txt")
print(response.result) # intermediate results
```
```python
from daytona import Daytona
daytona = Daytona()
sandbox = daytona.get("my-sandbox")
# Archive a stopped sandbox: the filesystem moves to object storage
# and no longer counts against disk quota
sandbox.stop()
sandbox.archive()
# Start restores the filesystem from object storage
sandbox.start()
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create()
# Prepare the environment to capture
sandbox.process.exec("pip install requests")
# Cold snapshot captures the filesystem of a stopped sandbox
sandbox.stop()
sandbox.create_snapshot("my-env-snapshot")
# Any number of new sandboxes can start from the same snapshot
clone = daytona.create(CreateSandboxFromSnapshotParams(snapshot="my-env-snapshot"))
response = clone.process.exec("pip show requests")
print(response.result) # requests is already installed
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="daytona-vm-small"))
# Write state to the filesystem
sandbox.fs.upload_file(b"intermediate results", "results.txt")
# Stop clears memory and offloads the filesystem to nearby storage
sandbox.stop()
# Start restores the filesystem exactly as it was
sandbox.start()
response = sandbox.process.exec("cat results.txt")
print(response.result) # intermediate results
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="daytona-vm-small"))
# Prepare the environment to capture
sandbox.process.exec("pip install requests")
# Cold snapshot captures the filesystem of a stopped sandbox
sandbox.stop()
sandbox.create_snapshot("my-vm-env-snapshot")
# Any number of new sandboxes can start from the same snapshot
clone = daytona.create(CreateSandboxFromSnapshotParams(snapshot="my-vm-env-snapshot"))
response = clone.process.exec("pip show requests")
print(response.result) # requests is already installed
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="windows-small"))
# Write state to the filesystem
sandbox.fs.upload_file(b"intermediate results", "results.txt")
# Stop clears memory and offloads the filesystem to nearby storage
sandbox.stop()
# Start restores the filesystem exactly as it was
sandbox.start()
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="windows-small"))
# Cold snapshot captures the filesystem of a stopped sandbox
sandbox.stop()
sandbox.create_snapshot("my-windows-env-snapshot")
# Any number of new sandboxes can start from the same snapshot
clone = daytona.create(CreateSandboxFromSnapshotParams(snapshot="my-windows-env-snapshot"))
```
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount
daytona = Daytona()
volume = daytona.volume.get("gpu-results", create=True)
# GPU sandboxes are ephemeral: mount a volume to keep results
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
snapshot="daytona-gpu",
ephemeral=True,
volumes=[VolumeMount(volume_id=volume.id, mount_path="/home/daytona/results")],
))
# Write results to the volume before the sandbox stops
sandbox.process.exec("cp model-output.json /home/daytona/results/")
# The sandbox is deleted on stop; the volume and its data persist
sandbox.stop()
```
## Memory persistence
Memory persistence keeps the runtime state of a sandbox: running processes, open connections, and everything loaded in RAM. A sandbox with preserved memory continues from the point it was frozen, with processes still running, instead of starting from a clean boot.
Sandboxes preserve their memory across **pause and resume** cycles, **snapshots created from a sandbox (hot snapshots)**, and **forks**. Memory state is cleared on every stop. Sandbox classes differ in whether memory can be preserved without keeping the sandbox running:
| **Sandbox class** | **Memory persistence** |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Container | Memory persists only while the sandbox is running. Set the [**auto-stop interval**](https://www.daytona.io/docs/en/sandboxes.md#auto-stop-interval) to `0` to run indefinitely, or relaunch processes after each start. Pause is not supported. |
| VM sandboxes (Linux VM and Windows) | Memory is preserved through [**pause / resume**](https://www.daytona.io/docs/en/sandboxes.md#pause--resume-sandboxes): pausing freezes the VM with memory intact, resuming continues all processes from the point they were frozen. Use [**hot snapshots**](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox) or [**forks**](https://www.daytona.io/docs/en/sandboxes.md#fork-sandboxes) to capture memory. |
| GPU | Ephemeral by design: deleted on stop, and memory is not preserved. Use [**volumes**](https://www.daytona.io/docs/en/volumes.md) to persist results of a GPU sandbox. |
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams, SessionExecuteRequest
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="daytona-vm-small"))
# Launch a process that holds state in memory
sandbox.process.create_session("server")
sandbox.process.execute_session_command("server", SessionExecuteRequest(
command="python3 -m http.server 8000",
run_async=True,
))
# Pause freezes the VM with filesystem and memory intact
sandbox.pause()
# Resume continues all processes from the point they were frozen
sandbox.start()
response = sandbox.process.exec("curl -s http://localhost:8000")
print(response.result) # the server is still running
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams, SessionExecuteRequest
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="daytona-vm-small"))
# Launch a process that holds state in memory
sandbox.process.create_session("server")
sandbox.process.execute_session_command("server", SessionExecuteRequest(
command="python3 -m http.server 8000",
run_async=True,
))
```
The SDKs create cold snapshots only. Create a hot snapshot of the running VM through the API to capture its filesystem and memory:
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/snapshot' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-vm-snapshot",
"includeMemory": true
}'
```
Sandboxes created from the hot snapshot start with the process already running:
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
clone = daytona.create(CreateSandboxFromSnapshotParams(snapshot="my-vm-snapshot"))
response = clone.process.exec("curl -s http://localhost:8000")
print(response.result)
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams, SessionExecuteRequest
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="daytona-vm-small"))
# Launch a process that holds state in memory
sandbox.process.create_session("server")
sandbox.process.execute_session_command("server", SessionExecuteRequest(
command="python3 -m http.server 8000",
run_async=True,
))
# Fork duplicates the filesystem and memory into an independent sandbox
fork = sandbox.fork(name="my-forked-sandbox")
response = fork.process.exec("curl -s http://localhost:8000")
print(response.result) # the process is running in the fork as well
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="windows-small"))
# Pause freezes the VM with filesystem and memory intact
sandbox.pause()
# Resume continues all processes from the point they were frozen
sandbox.start()
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="windows-small"))
```
The SDKs create cold snapshots only. Create a hot snapshot of the running VM through the API to capture its filesystem and memory:
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/snapshot' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-windows-snapshot",
"includeMemory": true
}'
```
Sandboxes created from the hot snapshot start with processes already running:
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
clone = daytona.create(CreateSandboxFromSnapshotParams(snapshot="my-windows-snapshot"))
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="windows-small"))
# Fork duplicates the filesystem and memory into an independent sandbox
fork = sandbox.fork(name="my-forked-sandbox")
```
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount
daytona = Daytona()
volume = daytona.volume.get("gpu-results", create=True)
# GPU sandboxes are ephemeral: mount a volume to keep results
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
snapshot="daytona-gpu",
ephemeral=True,
volumes=[VolumeMount(volume_id=volume.id, mount_path="/home/daytona/results")],
))
# Write results to the volume before the sandbox stops
sandbox.process.exec("cp model-output.json /home/daytona/results/")
# The sandbox is deleted on stop; the volume and its data persist
sandbox.stop()
```
## Persistence beyond a sandbox
Filesystem and memory persistence are tied to a single sandbox: deleting the sandbox deletes its state. To persist state beyond the sandbox itself, capture it as a **snapshot**, duplicate it into an independent sandbox with a **fork**, or store data outside any sandbox in a **volume**.
### Snapshots from a sandbox
[Snapshots created from a sandbox](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox) capture and persist the sandbox's current state, including the filesystem, installed packages, dependencies, and settings. The snapshot saves the state so you can restore it later, and any number of new sandboxes can start from the same snapshot. For snapshots built from an image or Dockerfile, which define a base environment rather than persist an existing sandbox, see [snapshots](https://www.daytona.io/docs/en/snapshots.md).
A snapshot created from a sandbox captures the filesystem (**cold snapshot**) or the filesystem and memory (**hot snapshot**), depending on the sandbox class:
- **Container sandboxes** can capture the filesystem only (**cold snapshot**)
- **VM sandboxes** (**Linux VM** and **Windows**) can capture the filesystem only (**cold snapshot**) or the filesystem and memory (**hot snapshot**): sandboxes created from a hot snapshot start with processes already running
Snapshots persist independently of the source sandbox: deleting the sandbox does not delete snapshots created from it.
### Forks
[Forking](https://www.daytona.io/docs/en/sandboxes.md#fork-sandboxes) duplicates a VM sandbox's filesystem and memory into a new, fully independent sandbox. Use forks to branch a live environment: run divergent experiments from the same state, test a risky change without touching the original, or hand each agent in a fleet an identical starting point.
The fork persists independently of the original: it can be started, stopped, and deleted without affecting it. Daytona tracks the fork lineage, forks can be forked again, and a parent cannot be deleted while it has active fork children.
### Volumes
[Volumes](https://www.daytona.io/docs/en/volumes.md) are S3-backed mounts that persist independently of any sandbox. Data written to a mounted volume survives sandbox deletion and is readable from other sandboxes that mount the same volume. Use volumes for datasets, model weights, build caches, and per-user or per-tenant data scoped with a `subpath`. For data already in your own object storage, [mount external storage](https://www.daytona.io/docs/en/mount-external-storage.md) directly instead of copying it in.
## Retention and lifecycle
Sandboxes in stopped, paused, and archived states are retained until you delete them. Retention costs are driven by the lifecycle state: see [billing](https://www.daytona.io/docs/en/billing.md) and [limits](https://www.daytona.io/docs/en/limits.md) for details.
Auto-stop and auto-pause are mutually exclusive: at most one of the two intervals may be non-zero. VM sandboxes default to auto-pause with auto-stop disabled; container and GPU sandboxes default to auto-stop.
| **Interval** | **Applies to** | **Default** | **Effect** |
| ------------------------------------------------------------------- | --------------------------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------- |
| [**Auto-stop**](https://www.daytona.io/docs/en/sandboxes.md#auto-stop-interval) | Container and GPU sandboxes | **`15`** minutes of inactivity | Stops the sandbox; container sandboxes preserve the filesystem, while GPU sandboxes are deleted |
| [**Auto-pause**](https://www.daytona.io/docs/en/sandboxes.md#auto-pause-interval) | VM sandboxes (Linux VM, Windows) | **`60`** minutes of inactivity | Pauses the VM sandbox, preserving filesystem and memory |
| [**Auto-archive**](https://www.daytona.io/docs/en/sandboxes.md#auto-archive-interval) | Container sandboxes | **`7`** days stopped (**`30`** days max) | Moves the filesystem to object storage |
| [**Auto-delete**](https://www.daytona.io/docs/en/sandboxes.md#auto-delete-interval) | All sandboxes | Disabled | Deletes the sandbox after it has been stopped |
| [**Wall-clock TTL**](https://www.daytona.io/docs/en/sandboxes.md#wall-clock-ttl) | All sandboxes | Disabled | Destroys the sandbox after a fixed wall-clock deadline, regardless of state |
## Opting out of persistence
Persistence is the default, not a requirement: a sandbox can opt out of it entirely. For workloads where state is not needed after the run, create an [ephemeral sandbox](https://www.daytona.io/docs/en/sandboxes.md#ephemeral-sandboxes). Ephemeral sandboxes are deleted as soon as they stop, discarding all state, and accrue no stopped-state disk billing.
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
# Ephemeral sandboxes are deleted as soon as they stop
sandbox = daytona.create(CreateSandboxFromSnapshotParams(ephemeral=True))
# Run the workload and read the results before the sandbox stops
response = sandbox.process.exec("python3 run.py")
print(response.result)
# Stop deletes the sandbox and all of its state
sandbox.stop()
```
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
# Auto-delete removes the sandbox after it has been stopped for 1 hour
sandbox = daytona.create(CreateSandboxFromSnapshotParams(auto_delete_interval=60))
# Set the interval to 0 to delete immediately on stop, same as ephemeral
sandbox.set_auto_delete_interval(0)
# Set the interval to -1 to disable auto-deletion
sandbox.set_auto_delete_interval(-1)
```
# Scale
Daytona sandboxes are designed to be created and operated at high volume. A single sandbox scales up by resizing its reserved resources, a fleet scales out by running many sandboxes at once, and each sandbox runs multiple processes concurrently.
Unlike request-based execution environments, a sandbox is a long-lived computer: processes are not terminated after a request completes, so running services, open connections, and background workers persist until the sandbox stops.
Scale operates along three dimensions:
| **Dimension** | **What scales** | **Mechanisms** |
| ------------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Sandbox (vertical) | vCPU, RAM, and disk of a single sandbox | • [**Custom resources**](https://www.daytona.io/docs/en/sandboxes.md#resources) • [**Resize**](https://www.daytona.io/docs/en/sandboxes.md#resize-sandboxes) |
| Fleet (horizontal) | Number of sandboxes running at the same time | • [**Snapshot fan-out**](https://www.daytona.io/docs/en/snapshots.md) • [**Fork**](https://www.daytona.io/docs/en/sandboxes.md#fork-sandboxes) • [**Linked sandboxes**](https://www.daytona.io/docs/en/sandboxes.md#linked-sandboxes) |
| Workload (concurrency) | Concurrent processes inside a single sandbox | • [**Sessions**](https://www.daytona.io/docs/en/process-code-execution.md#session-operations) • [**Async commands**](https://www.daytona.io/docs/en/process-code-execution.md#session-operations) • [**PTY sessions**](https://www.daytona.io/docs/en/pty.md) |
## Sandbox scaling
Sandbox scaling changes the resources of a single sandbox: **vCPU**, **memory**, and **disk**. Resources are reserved when the sandbox is created and can be changed later by resizing.
Resources are set differently depending on how the sandbox is created:
- **From an image**: set **`resources`** on the create parameters
- **From a snapshot**: the sandbox inherits the resources defined on the snapshot
Resizing changes the allocation of an existing sandbox. On a running sandbox, CPU and memory can be increased without interruption. Decreasing CPU or memory, or increasing disk, requires the sandbox to be stopped first. Disk can only grow, and GPU allocation cannot be resized. Every allocation must stay within your organization's per-sandbox limits.
Reserve CPU, memory, and disk when creating a sandbox from an image.
```python
from daytona import CreateSandboxFromImageParams, Daytona, Resources
daytona = Daytona()
# Reserve 2 vCPUs, 4GiB of RAM, and 8GiB of disk for the sandbox
sandbox = daytona.create(
CreateSandboxFromImageParams(
image="ubuntu:22.04",
resources=Resources(cpu=2, memory=4, disk=8),
)
)
```
```typescript
import { Daytona, Image } from '@daytona/sdk'
const daytona = new Daytona()
// Reserve 2 vCPUs, 4GiB of RAM, and 8GiB of disk for the sandbox
const sandbox = await daytona.create({
image: Image.base('ubuntu:22.04'),
resources: { cpu: 2, memory: 4, disk: 8 },
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
# Reserve 2 vCPUs, 4GiB of RAM, and 8GiB of disk for the sandbox
sandbox = daytona.create(
Daytona::CreateSandboxFromImageParams.new(
image: Daytona::Image.base('ubuntu:22.04'),
resources: Daytona::Resources.new(
cpu: 2,
memory: 4,
disk: 8
)
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
// Reserve 2 vCPUs, 4GiB of RAM, and 8GiB of disk for the sandbox
sandbox, err := client.Create(ctx, types.ImageParams{
Image: "ubuntu:22.04",
Resources: &types.Resources{
CPU: 2,
Memory: 4,
Disk: 8,
},
})
if err != nil {
// handle error
}
_ = sandbox
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromImageParams;
import io.daytona.sdk.model.Resources;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromImageParams params = new CreateSandboxFromImageParams();
params.setImage("ubuntu:22.04");
// Reserve 2 vCPUs, 4GiB of RAM, and 8GiB of disk for the sandbox
Resources resources = new Resources();
resources.setCpu(2);
resources.setMemory(4);
resources.setDisk(8);
params.setResources(resources);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
# Reserve 2 vCPUs, 4GiB of RAM, and 8GiB of disk for the sandbox
daytona create --cpu 2 --memory 4 --disk 8
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"buildInfo": {
"dockerfileContent": "FROM ubuntu:22.04\n"
},
"cpu": 2,
"memory": 4,
"disk": 8
}'
```
Change the resource allocation of an existing sandbox.
```python
from daytona import Daytona, Resources
daytona = Daytona()
sandbox = daytona.get("my-sandbox")
# CPU and memory can be increased while the sandbox is running
sandbox.resize(Resources(cpu=4, memory=8))
# Decreasing CPU or memory, or increasing disk, requires a stopped sandbox
sandbox.stop()
sandbox.resize(Resources(cpu=2, memory=4, disk=20))
sandbox.start()
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.get('my-sandbox')
// CPU and memory can be increased while the sandbox is running
await sandbox.resize({ cpu: 4, memory: 8 })
// Decreasing CPU or memory, or increasing disk, requires a stopped sandbox
await sandbox.stop()
await sandbox.resize({ cpu: 2, memory: 4, disk: 20 })
await sandbox.start()
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.get('my-sandbox')
# CPU and memory can be increased while the sandbox is running
sandbox.resize(Daytona::Resources.new(cpu: 4, memory: 8))
# Decreasing CPU or memory, or increasing disk, requires a stopped sandbox
sandbox.stop
sandbox.resize(Daytona::Resources.new(cpu: 2, memory: 4, disk: 20))
sandbox.start
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
sandbox, err := client.Get(ctx, "my-sandbox")
if err != nil {
// handle error
}
// CPU and memory can be increased while the sandbox is running
err = sandbox.Resize(ctx, &types.Resources{CPU: 4, Memory: 8})
if err != nil {
// handle error
}
// Decreasing CPU or memory, or increasing disk, requires a stopped sandbox
err = sandbox.Stop(ctx)
if err != nil {
// handle error
}
err = sandbox.Resize(ctx, &types.Resources{CPU: 2, Memory: 4, Disk: 20})
if err != nil {
// handle error
}
err = sandbox.Start(ctx)
if err != nil {
// handle error
}
}
```
```bash
# CPU and memory can be increased while the sandbox is running
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/resize' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"cpu": 4,
"memory": 8
}'
# Decreasing CPU or memory, or increasing disk, requires a stopped sandbox
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/stop' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/resize' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"cpu": 2,
"memory": 4,
"disk": 20
}'
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/start' \
--request POST \
--header 'Authorization: Bearer YOUR_API_KEY'
```
## Fleet scaling
Fleet scaling raises the number of sandboxes running at the same time. The unit of scale is the sandbox itself: instead of packing unrelated workloads into one environment, create one sandbox per user, task, or agent.
Sandboxes are isolated from each other, so a fleet scales linearly: each new sandbox adds capacity without contention, and a failure in one sandbox does not affect the others.
The mechanisms differ in what state each new sandbox starts with:
| **Mechanism** | **Starting state** | **Use** |
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| [**Snapshot fan-out**](https://www.daytona.io/docs/en/snapshots.md) | Filesystem from the snapshot; hot snapshots add memory state | Any number of identical environments from one prepared snapshot |
| [**Fork**](https://www.daytona.io/docs/en/sandboxes.md#fork-sandboxes) | Filesystem and memory of a running VM sandbox | Branch a live environment into independent copies |
| [**Linked sandboxes**](https://www.daytona.io/docs/en/sandboxes.md#linked-sandboxes) | Fresh environment, co-located with a parent on the same runner | Coordinated groups with a local network between parent and children |
**Snapshot fan-out** is the default pattern: prepare the environment once, capture it as a snapshot, and create any number of sandboxes from it. Each sandbox starts with the environment intact, with nothing to reinstall. Sandboxes created from a [hot snapshot](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox) start with processes already running.
```python
import asyncio
from daytona import AsyncDaytona, CreateSandboxFromSnapshotParams
async def run_task(daytona: AsyncDaytona, shard: int) -> str:
# Each task gets its own isolated, ephemeral sandbox
sandbox = await daytona.create(
CreateSandboxFromSnapshotParams(snapshot="my-env-snapshot", ephemeral=True)
)
response = await sandbox.process.exec(f"python3 run.py --shard {shard}")
# Stop deletes the ephemeral sandbox
await sandbox.stop()
return response.result
async def main():
async with AsyncDaytona() as daytona:
# Create and run 20 sandboxes concurrently from the same snapshot
results = await asyncio.gather(*(run_task(daytona, i) for i in range(20)))
print(results)
asyncio.run(main())
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
async function runTask(shard: number): Promise {
// Each task gets its own isolated, ephemeral sandbox
const sandbox = await daytona.create({
snapshot: 'my-env-snapshot',
ephemeral: true,
})
const response = await sandbox.process.executeCommand(
`python3 run.py --shard ${shard}`
)
// Stop deletes the ephemeral sandbox
await sandbox.stop()
return response.result
}
// Create and run 20 sandboxes concurrently from the same snapshot
const results = await Promise.all(
Array.from({ length: 20 }, (_, i) => runTask(i))
)
console.log(results)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
# Create and run 20 sandboxes concurrently from the same snapshot
results = 20.times.map do |shard|
Thread.new do
# Each task gets its own isolated, ephemeral sandbox
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'my-env-snapshot',
ephemeral: true
)
)
response = sandbox.process.exec(command: "python3 run.py --shard #{shard}")
# Stop deletes the ephemeral sandbox
sandbox.stop
response.result
end
end.map(&:value)
puts results
```
```go
package main
import (
"context"
"fmt"
"sync"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
results := make([]string, 20)
var wg sync.WaitGroup
// Create and run 20 sandboxes concurrently from the same snapshot
for i := 0; i < 20; i++ {
wg.Add(1)
go func(shard int) {
defer wg.Done()
// Each task gets its own isolated, ephemeral sandbox
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: "my-env-snapshot",
SandboxBaseParams: types.SandboxBaseParams{
Ephemeral: true,
},
})
if err != nil {
return
}
response, err := sandbox.Process.ExecuteCommand(
ctx, fmt.Sprintf("python3 run.py --shard %d", shard),
)
if err != nil {
return
}
// Stop deletes the ephemeral sandbox
_ = sandbox.Stop(ctx)
results[shard] = response.Result
}(i)
}
wg.Wait()
fmt.Println(results)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.IntStream;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
// Create and run 20 sandboxes concurrently from the same snapshot
List> futures = IntStream.range(0, 20)
.mapToObj(shard -> CompletableFuture.supplyAsync(() -> {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my-env-snapshot");
params.setAutoDeleteInterval(0); // ephemeral
// Each task gets its own isolated, ephemeral sandbox
Sandbox sandbox = daytona.create(params);
ExecuteResponse response = sandbox.getProcess()
.executeCommand("python3 run.py --shard " + shard);
// Stop deletes the ephemeral sandbox
sandbox.stop();
return response.getResult();
}))
.toList();
List results = new ArrayList<>();
for (CompletableFuture future : futures) {
results.add(future.join());
}
System.out.println(results);
}
}
}
```
```bash
# Create sandboxes from the same snapshot (run concurrently as needed)
for i in $(seq 0 19); do
daytona create --snapshot my-env-snapshot --auto-delete 0 &
done
wait
```
```bash
# Create each sandbox from the same snapshot (issue requests concurrently as needed)
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "my-env-snapshot",
"autoDeleteInterval": 0
}'
```
**Forking** duplicates a running VM sandbox, filesystem and memory included, into an independent sandbox. Where snapshot fan-out distributes a prepared baseline, forks branch live state: each fork continues from the exact point the original was at. Forking is supported for [VM sandboxes](https://www.daytona.io/docs/en/sandboxes.md#vm-sandboxes) only.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="daytona-vm-small"))
# Prepare live state once: running processes, loaded caches, open connections
sandbox.process.exec("python3 warmup.py")
# Each fork continues from the same live state, fully independent
forks = [sandbox.fork(name=f"agent-{i}") for i in range(5)]
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({ snapshot: 'daytona-vm-small' })
// Prepare live state once: running processes, loaded caches, open connections
await sandbox.process.executeCommand('python3 warmup.py')
// Each fork continues from the same live state, fully independent
const forks = await Promise.all(
Array.from({ length: 5 }, (_, i) =>
sandbox.fork({ name: `agent-${i}` })
)
)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(snapshot: 'daytona-vm-small')
)
# Prepare live state once: running processes, loaded caches, open connections
sandbox.process.exec(command: 'python3 warmup.py')
# Each fork continues from the same live state, fully independent
forks = 5.times.map { |i| sandbox.fork(name: "agent-#{i}") }
```
```go
package main
import (
"context"
"fmt"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: "daytona-vm-small",
})
if err != nil {
// handle error
}
// Prepare live state once: running processes, loaded caches, open connections
_, err = sandbox.Process.ExecuteCommand(ctx, "python3 warmup.py")
if err != nil {
// handle error
}
// Each fork continues from the same live state, fully independent
forks := make([]*daytona.Sandbox, 5)
for i := 0; i < 5; i++ {
name := fmt.Sprintf("agent-%d", i)
forks[i], err = sandbox.Fork(ctx, &name)
if err != nil {
// handle error
}
}
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import java.util.ArrayList;
import java.util.List;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("daytona-vm-small");
Sandbox sandbox = daytona.create(params);
// Prepare live state once: running processes, loaded caches, open connections
sandbox.getProcess().executeCommand("python3 warmup.py");
// Each fork continues from the same live state, fully independent
List forks = new ArrayList<>();
for (int i = 0; i < 5; i++) {
forks.add(sandbox.fork("agent-" + i, 60));
}
}
}
}
```
```bash
# Fork a running VM sandbox (repeat for each independent copy)
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/fork' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "agent-0"
}'
```
**Linked sandboxes** attach ephemeral child sandboxes to a parent. Children are scheduled on the same runner as the parent and share a link network, so the group communicates over local connections. One parent may have many children, and deleting the parent deletes all of them.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
parent = daytona.create()
# Children are co-located with the parent and share a link network
children = [
daytona.create(
CreateSandboxFromSnapshotParams(
linked_sandbox=parent.id,
ephemeral=True,
)
)
for _ in range(3)
]
# Each sandbox on the link network is reachable by name or ID
response = children[0].process.exec(f"curl http://{parent.name}:3000/")
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const parent = await daytona.create()
// Children are co-located with the parent and share a link network
const children = await Promise.all(
Array.from({ length: 3 }, () =>
daytona.create({
linkedSandbox: parent.id,
ephemeral: true,
})
)
)
// Each sandbox on the link network is reachable by name or ID
const response = await children[0].process.executeCommand(
`curl http://${parent.name}:3000/`
)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
parent = daytona.create
# Children are co-located with the parent and share a link network
children = 3.times.map do
daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
linked_sandbox: parent.id,
ephemeral: true
)
)
end
# The link network registers each sandbox under its name and ID as DNS aliases.
# The Ruby SDK does not expose the sandbox name, so address the parent by ID.
response = children[0].process.exec(command: "curl http://#{parent.id}:3000/")
```
```go
package main
import (
"context"
"fmt"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
parent, err := client.Create(ctx, types.SnapshotParams{})
if err != nil {
// handle error
}
// Children are co-located with the parent and share a link network
children := make([]*daytona.Sandbox, 3)
for i := 0; i < 3; i++ {
children[i], err = client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
LinkedSandbox: parent.ID,
Ephemeral: true,
},
})
if err != nil {
// handle error
}
}
// Each sandbox on the link network is reachable by name or ID
response, err := children[0].Process.ExecuteCommand(
ctx, fmt.Sprintf("curl http://%s:3000/", parent.Name),
)
if err != nil {
// handle error
}
fmt.Println(response.Result)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
import java.util.ArrayList;
import java.util.List;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Sandbox parent = daytona.create();
// Children are co-located with the parent and share a link network
List children = new ArrayList<>();
for (int i = 0; i < 3; i++) {
CreateSandboxFromSnapshotParams childParams = new CreateSandboxFromSnapshotParams();
childParams.setLinkedSandbox(parent.getId());
childParams.setAutoDeleteInterval(0); // linked sandboxes must be ephemeral
children.add(daytona.create(childParams));
}
// Each sandbox on the link network is reachable by name or ID
ExecuteResponse response = children.get(0).getProcess()
.executeCommand("curl http://" + parent.getName() + ":3000/");
}
}
}
```
```bash
# Create parent sandbox
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{}'
# Create linked child sandboxes (replace PARENT_SANDBOX_ID; repeat for each child)
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"linkedSandbox": "PARENT_SANDBOX_ID",
"autoDeleteInterval": 0
}'
```
## Workload concurrency
Workload concurrency runs multiple processes inside a single sandbox. A single sandbox can run an API server, a database, and background workers side by side: all processes share the sandbox's filesystem and network, and exposed ports are reachable through [previews](https://www.daytona.io/docs/en/preview.md).
A sandbox runs concurrent processes through [sessions](https://www.daytona.io/docs/en/process-code-execution.md#session-operations). Each session is an independent shell with its own state: working directory, environment variables, and command history.
Commands within a session run sequentially, so parallelism comes from running multiple sessions. Commands started with `run_async` return immediately and run in the background, and [PTY sessions](https://www.daytona.io/docs/en/pty.md) add interactive terminals.
Run multiple independent shells in the same sandbox so processes can execute in parallel.
```python
from daytona import Daytona, SessionExecuteRequest
daytona = Daytona()
sandbox = daytona.create()
# Each session is an independent shell inside the same sandbox
sandbox.process.create_session("server")
sandbox.process.create_session("worker")
# The server runs in the background while the worker session stays free
sandbox.process.execute_session_command("server", SessionExecuteRequest(
command="python3 -m http.server 8000",
run_async=True,
))
response = sandbox.process.execute_session_command("worker", SessionExecuteRequest(
command="curl -s http://localhost:8000",
))
print(response.output)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create()
// Each session is an independent shell inside the same sandbox
await sandbox.process.createSession('server')
await sandbox.process.createSession('worker')
// The server runs in the background while the worker session stays free
await sandbox.process.executeSessionCommand('server', {
command: 'python3 -m http.server 8000',
runAsync: true,
})
const response = await sandbox.process.executeSessionCommand('worker', {
command: 'curl -s http://localhost:8000',
})
console.log(response.output)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create
# Each session is an independent shell inside the same sandbox
sandbox.process.create_session('server')
sandbox.process.create_session('worker')
# The server runs in the background while the worker session stays free
sandbox.process.execute_session_command(
session_id: 'server',
req: Daytona::SessionExecuteRequest.new(
command: 'python3 -m http.server 8000',
run_async: true
)
)
response = sandbox.process.execute_session_command(
session_id: 'worker',
req: Daytona::SessionExecuteRequest.new(
command: 'curl -s http://localhost:8000'
)
)
puts response.output
```
```go
package main
import (
"context"
"fmt"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
sandbox, err := client.Create(ctx, types.SnapshotParams{})
if err != nil {
// handle error
}
// Each session is an independent shell inside the same sandbox
err = sandbox.Process.CreateSession(ctx, "server")
if err != nil {
// handle error
}
err = sandbox.Process.CreateSession(ctx, "worker")
if err != nil {
// handle error
}
// The server runs in the background while the worker session stays free
_, err = sandbox.Process.ExecuteSessionCommand(
ctx, "server", "python3 -m http.server 8000", true, false,
)
if err != nil {
// handle error
}
response, err := sandbox.Process.ExecuteSessionCommand(
ctx, "worker", "curl -s http://localhost:8000", false, false,
)
if err != nil {
// handle error
}
fmt.Println(response["stdout"])
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.SessionExecuteRequest;
import io.daytona.sdk.model.SessionExecuteResponse;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Sandbox sandbox = daytona.create();
// Each session is an independent shell inside the same sandbox
sandbox.getProcess().createSession("server");
sandbox.getProcess().createSession("worker");
// The server runs in the background while the worker session stays free
sandbox.getProcess().executeSessionCommand(
"server",
new SessionExecuteRequest("python3 -m http.server 8000", true)
);
SessionExecuteResponse response = sandbox.getProcess().executeSessionCommand(
"worker",
new SessionExecuteRequest("curl -s http://localhost:8000", false)
);
System.out.println(response.getOutput());
}
}
}
```
```bash
# Create independent sessions
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session' \
--request POST \
--header 'Content-Type: application/json' \
--data '{"sessionId": "server"}'
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session' \
--request POST \
--header 'Content-Type: application/json' \
--data '{"sessionId": "worker"}'
# Start the server in the background
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session/server/exec' \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"command": "python3 -m http.server 8000",
"runAsync": true
}'
# Run a command in the worker session
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session/worker/exec' \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"command": "curl -s http://localhost:8000"
}'
```
Start a command asynchronously, then poll its status by command ID.
```python
from daytona import Daytona, SessionExecuteRequest
daytona = Daytona()
sandbox = daytona.create()
sandbox.process.create_session("build")
# Async commands return immediately with a command ID
command = sandbox.process.execute_session_command("build", SessionExecuteRequest(
command="make build",
run_async=True,
))
# Check on the command later by its ID
status = sandbox.process.get_session_command("build", command.cmd_id)
print(status.exit_code)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create()
await sandbox.process.createSession('build')
// Async commands return immediately with a command ID
const command = await sandbox.process.executeSessionCommand('build', {
command: 'make build',
runAsync: true,
})
// Check on the command later by its ID
const status = await sandbox.process.getSessionCommand('build', command.cmdId!)
console.log(status.exitCode)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create
sandbox.process.create_session('build')
# Async commands return immediately with a command ID
command = sandbox.process.execute_session_command(
session_id: 'build',
req: Daytona::SessionExecuteRequest.new(
command: 'make build',
run_async: true
)
)
# Check on the command later by its ID
status = sandbox.process.get_session_command(
session_id: 'build',
command_id: command.cmd_id
)
puts status.exit_code
```
```go
package main
import (
"context"
"fmt"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
sandbox, err := client.Create(ctx, types.SnapshotParams{})
if err != nil {
// handle error
}
err = sandbox.Process.CreateSession(ctx, "build")
if err != nil {
// handle error
}
// Async commands return immediately with a command ID
command, err := sandbox.Process.ExecuteSessionCommand(
ctx, "build", "make build", true, false,
)
if err != nil {
// handle error
}
cmdID := command["id"].(string)
// Check on the command later by its ID
status, err := sandbox.Process.GetSessionCommand(ctx, "build", cmdID)
if err != nil {
// handle error
}
fmt.Println(status["exitCode"])
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.Command;
import io.daytona.sdk.model.SessionExecuteRequest;
import io.daytona.sdk.model.SessionExecuteResponse;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Sandbox sandbox = daytona.create();
sandbox.getProcess().createSession("build");
// Async commands return immediately with a command ID
SessionExecuteResponse command = sandbox.getProcess().executeSessionCommand(
"build",
new SessionExecuteRequest("make build", true)
);
// Check on the command later by its ID
Command status = sandbox.getProcess().getSessionCommand(
"build",
command.getCmdId()
);
System.out.println(status.getExitCode());
}
}
}
```
```bash
# Create a session
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session' \
--request POST \
--header 'Content-Type: application/json' \
--data '{"sessionId": "build"}'
# Start a background command (returns command ID immediately)
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session/build/exec' \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"command": "make build",
"runAsync": true
}'
# Check command status by ID
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/session/build/command/{commandId}'
```
## Capacity and throughput
Fleet size and creation rate operate within your organization's limits. Both are [tier-based](https://www.daytona.io/docs/en/limits.md#tiers) and raised by verification steps or by contacting support.
| **Control** | **What it limits** | **Scope** |
| ------------------------------------------------------------------ | ----------------------------------------------------------------------- | -------------------------------------------------- |
| [**Compute pool**](https://www.daytona.io/docs/en/limits.md#resources) | Total vCPU, RAM, and storage across all running sandboxes | Organization, per region and sandbox class |
| [**Per-sandbox limits**](https://www.daytona.io/docs/en/limits.md) | Maximum vCPU, RAM, and storage of a single sandbox | Sandbox |
| [**Rate limits**](https://www.daytona.io/docs/en/limits.md#rate-limits) | Sandbox creation, lifecycle operations, and API requests per minute | Organization |
To scale beyond the shared compute pool, [bring your own compute (BYOC)](https://www.daytona.io/docs/en/bring-your-own-compute.md) attaches your own runner nodes in custom regions, which have no concurrent resource usage limits.
### Reclaiming capacity
The compute pool is shared by running sandboxes only: stopped, paused, and archived sandboxes free their reserved CPU and memory back to the pool. A fleet can therefore be much larger than the pool, as long as the concurrently running subset fits.
Reclamation runs automatically. [Ephemeral sandboxes](https://www.daytona.io/docs/en/sandboxes.md#ephemeral-sandboxes) delete themselves on stop, and the [auto-stop, auto-pause, and auto-delete intervals](https://www.daytona.io/docs/en/sandboxes.md#automated-lifecycle-management) reclaim capacity from idle sandboxes without manual intervention. See [persistence](https://www.daytona.io/docs/en/persistence.md#retention-and-lifecycle) for details.
### Operating at scale
At high creation rates, two practices keep an application within its rate limits.
- Handle [rate limit errors](https://www.daytona.io/docs/en/limits.md#rate-limit-errors): the SDKs raise **`DaytonaRateLimitError`** when a limit is exceeded, and the returned headers indicate how long to wait before retrying the request.
- Use [webhooks](https://www.daytona.io/docs/en/webhooks.md) to track sandbox state changes instead of polling. Webhooks deliver state changes as they happen, so no requests are spent checking for status.
# Snapshots
Snapshots are persistent, point-in-time captures of sandbox state, including the filesystem, installed packages, dependencies, and settings. A snapshot saves a sandbox's state so you can restore it later, and any number of new sandboxes can start from the same snapshot.
Daytona provides default snapshots for creating sandboxes. You can also create snapshots from images, capture the state of existing sandboxes, or create warm pools for a snapshot:
- [**Create snapshots from an image**](#create-snapshots): define the base operating system, language runtimes, packages, and project-level setup in an image or Dockerfile, and Daytona builds it into a snapshot you can use to create sandboxes
- [**Create snapshots from a sandbox**](#create-snapshot-from-sandbox): captures and persists a sandbox's current state; container sandboxes capture filesystem state only (**cold snapshots**), VM sandboxes capture filesystem and memory state (**hot snapshots**)
- [**Warm pools**](https://www.daytona.io/docs/en/warm-pools.md): keep a configured number of pre-created, running sandboxes built from a snapshot; matching sandbox create requests claim one warm sandbox from the pool instantly instead of provisioning a new sandbox
## Default snapshots
| **Snapshot** | **vCPU** | **Memory** | **Storage** | **GPU** | **Sandbox Class** |
| ----------------------- | -------- | ---------- | ----------- | ------- | ----------------- |
| **`daytona-small`** | 1 | 1GiB | 3GiB | | Container |
| **`daytona-medium`** | 2 | 4GiB | 8GiB | | Container |
| **`daytona-large`** | 4 | 8GiB | 10GiB | | Container |
| **`daytona-gpu`** | 1 | 1GiB | 1GiB | 1 | GPU |
| **`daytona-vm-small`** | 1 | 1GiB | 3GiB | | Linux VM |
| **`daytona-vm-medium`** | 2 | 4GiB | 8GiB | | Linux VM |
| **`daytona-vm-large`** | 4 | 8GiB | 10GiB | | Linux VM |
| **`windows-small`** | 1 | 4GiB | 30GiB | | Windows |
| **`windows-medium`** | 2 | 8GiB | 50GiB | | Windows |
| **`windows-large`** | 4 | 16GiB | 50GiB | | Windows |
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click
3. Select a **`snapshot`**
4. Click
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="daytona-small",
)
)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: 'daytona-small',
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'daytona-small'
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
params := types.SnapshotParams{
Snapshot: "daytona-small",
}
_, _ = client.Create(ctx, params)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("daytona-small");
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create --snapshot daytona-small
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "daytona-small"
}'
```
Default snapshots include pre-installed Python and Node.js packages.
| **Package** | **Version** |
| ---------------------- | ----------- |
| **`anthropic`** | v0.120.2 |
| **`beautifulsoup4`** | v4.14.3 |
| **`claude-agent-sdk`** | v0.2.130 |
| **`openai-agents`** | v0.19.4 |
| **`daytona`** | v0.203.0 |
| **`django`** | v6.0.1 |
| **`flask`** | v3.1.2 |
| **`huggingface-hub`** | v0.36.0 |
| **`instructor`** | v1.14.4 |
| **`keras`** | v3.13.0 |
| **`langchain`** | v1.2.7 |
| **`llama-index`** | v0.14.13 |
| **`matplotlib`** | v3.10.8 |
| **`numpy`** | v2.4.1 |
| **`ollama`** | v0.6.1 |
| **`openai`** | v2.53.0 |
| **`opencv-python`** | v4.13.0.90 |
| **`pandas`** | v2.3.3 |
| **`pillow`** | v12.1.0 |
| **`pipx`** | v1.8.0 |
| **`pydantic-ai`** | v1.47.0 |
| **`python-lsp-server`** | v1.14.0 |
| **`requests`** | v2.32.5 |
| **`scikit-learn`** | v1.8.0 |
| **`scipy`** | v1.17.0 |
| **`seaborn`** | v0.13.2 |
| **`sqlalchemy`** | v2.0.46 |
| **`torch`** | v2.10.0 |
| **`transformers`** | v4.57.6 |
| **`uv`** | v0.9.26 |
| **Package** | **Version** |
| -------------------------------- | ----------- |
| **`@anthropic-ai/claude-code`** | v2.1.220 |
| **`@openai/codex`** | v0.146.0 |
| **`bun`** | v1.3.6 |
| **`openclaw`** | v2026.7.1-2 |
| **`opencode-ai`** | v1.18.14 |
| **`ts-node`** | v10.9.2 |
| **`typescript`** | v5.9.3 |
| **`typescript-language-server`** | v5.1.3 |
## Create snapshots
Create a snapshot.
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click
3. Enter the snapshot **`name`** and **`image`** of any publicly accessible image or container registry
- **Snapshot name**: identifier used to reference the snapshot
- **Snapshot image**: base image for the snapshot, must include either a tag or a digest (e.g., **`ubuntu:22.04`**); the **`latest`**/**`lts`**/**`stable`** tags are not supported
4. Click
```python
from daytona import Daytona, CreateSnapshotParams
daytona = Daytona()
snapshot = daytona.snapshot.create(
CreateSnapshotParams(name="my-awesome-snapshot", image="ubuntu:22.04"),
)
```
```typescript
import { Daytona } from "@daytona/sdk";
const daytona = new Daytona();
const snapshot = await daytona.snapshot.create({
name: "my-awesome-snapshot",
image: "python:3.12",
});
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
snapshot = daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(name: 'my-awesome-snapshot', image: 'python:3.12')
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
snapshot, logCh, _ := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "my-awesome-snapshot",
Image: "python:3.12",
})
for range logCh {
}
_ = snapshot
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.model.Snapshot;
final class CreateSnapshot {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Snapshot snapshot = daytona.snapshot().create("my-awesome-snapshot", "python:3.12");
}
}
}
```
```bash
daytona snapshot create my-awesome-snapshot --image python:3.11-slim --cpu 2 --memory 4
```
```bash
curl https://app.daytona.io/api/snapshots \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
--data '{
"name": "my-awesome-snapshot",
"imageName": "python:3.11-slim",
"cpu": 2,
"memory": 4
}'
```
## VM snapshots
Daytona provides methods to create VM snapshots for **Linux VM** and **Windows**.
VM snapshots are used to create [VM sandboxes](https://www.daytona.io/docs/en/sandboxes.md#vm-sandboxes). VM snapshots are distinct from container snapshots and cannot be used to create container sandboxes. VM snapshots support VM-only capabilities such as [creating a snapshot from a sandbox](#create-snapshot-from-sandbox).
Create a Linux VM snapshot.
:::note
Linux VM snapshots must be created from an existing image reference. Dockerfile and [declarative image](https://www.daytona.io/docs/en/declarative-builder.md) builds are not supported for the Linux VM sandbox class and fail during the build step. To use a custom image, push it to a public or [private registry](#snapshots-from-private-registries) and create the snapshot from that image, or [create a snapshot from a sandbox](#create-snapshot-from-sandbox).
:::
1. Create a snapshot from an **`image`**
2. Set the snapshot's sandbox class to **`LINUX_VM`**
```python
from daytona import Daytona, CreateSnapshotParams, SandboxClass
daytona = Daytona()
snapshot = daytona.snapshot.create(
CreateSnapshotParams(
name="my-vm-snapshot",
image="ubuntu:22.04",
sandbox_class=SandboxClass.LINUX_VM,
)
)
```
```typescript
import { Daytona, SandboxClass } from "@daytona/sdk";
const daytona = new Daytona();
const snapshot = await daytona.snapshot.create({
name: "my-vm-snapshot",
image: "ubuntu:22.04",
sandboxClass: SandboxClass.LINUX_VM,
});
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
snapshot = daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(
name: 'my-vm-snapshot',
image: 'ubuntu:22.04',
sandbox_class: DaytonaApiClient::SandboxClass::LINUX_VM
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
sandboxClass := types.SandboxClassLinuxVM
snapshot, logCh, _ := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "my-vm-snapshot",
Image: "ubuntu:22.04",
SandboxClass: &sandboxClass,
})
for range logCh {
}
_ = snapshot
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.api.client.model.SandboxClass;
import io.daytona.sdk.model.Snapshot;
final class CreateVmSnapshot {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Snapshot snapshot = daytona.snapshot().create("my-vm-snapshot", "ubuntu:22.04", SandboxClass.LINUX_VM);
}
}
}
```
```bash
daytona snapshot create my-vm-snapshot --image ubuntu:22.04 --sandbox-class linux-vm
```
```bash
curl https://app.daytona.io/api/snapshots \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
--data '{
"name": "my-vm-snapshot",
"imageName": "ubuntu:22.04",
"sandboxClass": "linux-vm"
}'
```
Windows snapshots are used to create [Windows sandboxes](https://www.daytona.io/docs/en/sandboxes.md#vm-sandboxes). They cannot be created from a base image. They are produced only through the [snapshot from sandbox](#create-snapshot-from-sandbox) by starting from an existing Windows sandbox and capturing its current state as a snapshot.
## GPU snapshots
Create a GPU snapshot. GPU snapshots are used to create [GPU sandboxes](https://www.daytona.io/docs/en/sandboxes.md#gpu-sandboxes).
GPU snapshots on shared regions belong to the [Earth region](https://www.daytona.io/docs/en/regions.md#earth-region). Daytona selects the shared region, ignores the requested `regionId`, and reports `earth` in the snapshot `regionIds`. GPU snapshots on dedicated and custom regions keep their real region ID.
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click
3. Enter the snapshot **`name`** and **`image`**
4. Select the **`Allocate GPU`** checkbox
5. Specify the **`GPU type`**(s):
- **`NVIDIA H100`**
- **`NVIDIA H200`**
- **`NVIDIA B200`**
- **`NVIDIA B300`**
- **`NVIDIA RTX PRO 6000`**
- **`NVIDIA RTX 4090`**
- **`NVIDIA RTX 5090`**
- **`AMD Instinct MI355X`**
6. Click
```python
from daytona import CreateSnapshotParams, Daytona, Image, Resources
daytona = Daytona()
snapshot = daytona.snapshot.create(
CreateSnapshotParams(
name="my-gpu-snapshot",
image=Image.base("python:3.12"),
resources=Resources(cpu=1, memory=1, disk=1, gpu=1),
),
)
```
```typescript
import { Daytona } from "@daytona/sdk";
const daytona = new Daytona();
const snapshot = await daytona.snapshot.create({
name: "my-gpu-snapshot",
image: "python:3.12",
resources: { cpu: 1, memory: 1, disk: 1, gpu: 1 },
});
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
snapshot = daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(
name: 'my-gpu-snapshot',
image: 'python:3.12',
resources: Daytona::Resources.new(cpu: 1, memory: 1, disk: 1, gpu: 1)
)
)
```
```go
package main
import (
"context"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, _ := daytona.NewClient()
ctx := context.Background()
snapshot, logCh, _ := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "my-gpu-snapshot",
Image: "python:3.12",
Resources: &types.Resources{
CPU: 1,
Memory: 1,
Disk: 1,
GPU: 1,
},
})
for range logCh {
}
_ = snapshot
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Image;
import io.daytona.sdk.model.Resources;
import io.daytona.sdk.model.Snapshot;
final class CreateGpuSnapshot {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Resources resources = new Resources();
resources.setCpu(1);
resources.setMemory(1);
resources.setDisk(1);
resources.setGpu(1);
Snapshot snapshot = daytona.snapshot().create(
"my-gpu-snapshot",
Image.base("python:3.12"),
resources,
null
);
}
}
}
```
```bash
curl https://app.daytona.io/api/snapshots \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
--data '{
"name": "my-gpu-snapshot",
"imageName": "python:3.12",
"cpu": 1,
"memory": 1,
"disk": 1,
"gpu": 1
}'
```
## Create snapshot from sandbox
Create a snapshot from a running or stopped sandbox.
Container sandboxes capture filesystem state only (**cold snapshot**):
| **Snapshot type** | **Include memory** | **Snapshot contents** | **Required sandbox state** |
| ----------------- | --------------------- | --------------------- | -------------------------- |
| Cold | **`false`** (default) | Filesystem only | Stopped |
```python
sandbox.create_snapshot("my-snapshot")
```
```typescript
await sandbox.createSnapshot('my-snapshot')
```
```ruby
sandbox.create_snapshot(name: 'my-snapshot')
```
```go
err := sandbox.CreateSnapshot(ctx, "my-snapshot")
if err != nil {
return err
}
```
```java
sandbox.createSnapshot("my-snapshot");
```
```bash
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/snapshot' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-snapshot",
"includeMemory": false
}'
```
Linux VM sandboxes capture filesystem state only (**cold snapshot**) or filesystem and memory state (**hot snapshot**) through the `includeMemory` parameter:
| **Snapshot type** | **Include memory** | **Snapshot contents** | **Required sandbox state** |
| ----------------- | --------------------- | --------------------- | -------------------------- |
| Cold | **`false`** (default) | Filesystem only | Stopped |
| Hot | **`true`** | Filesystem and memory | Started |
```python
# Cold snapshot (filesystem only, sandbox stopped)
sandbox.create_snapshot("my-snapshot")
```
```typescript
// Cold snapshot (filesystem only, sandbox stopped)
await sandbox.createSnapshot('my-snapshot')
```
```ruby
# Cold snapshot (filesystem only, sandbox stopped)
sandbox.create_snapshot(name: 'my-snapshot')
```
```go
// Cold snapshot (filesystem only, sandbox stopped)
err := sandbox.CreateSnapshot(ctx, "my-snapshot")
if err != nil {
return err
}
```
```java
// Cold snapshot (filesystem only, sandbox stopped)
sandbox.createSnapshot("my-snapshot");
```
```bash
# Cold snapshot (filesystem only, sandbox stopped)
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/snapshot' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-snapshot",
"includeMemory": false
}'
# Hot snapshot (filesystem and memory, sandbox running)
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/snapshot' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-vm-snapshot",
"includeMemory": true
}'
```
Windows sandboxes capture filesystem state only (**cold snapshot**) or filesystem and memory state (**hot snapshot**) through the `includeMemory` parameter:
| **Snapshot type** | **Include memory** | **Snapshot contents** | **Required sandbox state** |
| ----------------- | --------------------- | --------------------- | -------------------------- |
| Cold | **`false`** (default) | Filesystem only | Stopped |
| Hot | **`true`** | Filesystem and memory | Started |
```python
# Cold snapshot (filesystem only, sandbox stopped)
sandbox.create_snapshot("my-snapshot")
```
```typescript
// Cold snapshot (filesystem only, sandbox stopped)
await sandbox.createSnapshot('my-snapshot')
```
```ruby
# Cold snapshot (filesystem only, sandbox stopped)
sandbox.create_snapshot(name: 'my-snapshot')
```
```go
// Cold snapshot (filesystem only, sandbox stopped)
err := sandbox.CreateSnapshot(ctx, "my-snapshot")
if err != nil {
return err
}
```
```java
// Cold snapshot (filesystem only, sandbox stopped)
sandbox.createSnapshot("my-snapshot");
```
```bash
# Cold snapshot (filesystem only, sandbox stopped)
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/snapshot' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-snapshot",
"includeMemory": false
}'
# Hot snapshot (filesystem and memory, sandbox running)
curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/snapshot' \
--request POST \
--header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"name": "my-vm-snapshot",
"includeMemory": true
}'
```
## Snapshots from private registries
Create a snapshot from images from private container registries.
1. Go to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries)
2. Click and select your provider:
- [Docker Hub](#docker-hub)
- [Google Artifact Registry](#google-artifact-registry)
- [GitHub Container Registry](#github-container-registry)
- [Amazon ECR](#amazon-elastic-container-registry)
3. Enter the required fields
4. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
5. Click
6. Enter the snapshot **`name`** and the full **`image`** reference, including the registry host and repository (e.g. **`my-registry.com//custom-alpine:3.21`**)
#### Docker Hub
Create a snapshot from Docker Hub images.
1. Go to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries)
2. Click and select the **Docker Hub** tab
3. Input the following fields:
- **Username**: your Docker Hub username (the account with access to the image)
- **Personal Access Token**: a [Docker Hub PAT](https://docs.docker.com/security/access-tokens/); not your account password
- **Registry URL**: auto-filled with **`docker.io`** and not shown in the form
4. Create the snapshot using the full image reference
**`docker.io//:`**
#### Google Artifact Registry
Create a snapshot from images from Google Artifact Registry.
1. Go to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries),
2. Click and select the **Google** tab
2. Input the following fields:
- **Registry URL**: the base URL for your region
**`https://-docker.pkg.dev`**
- **Service Account JSON Key**: the contents of your service account key JSON file
- **Google Cloud Project ID**: your GCP project ID
- **Username**: auto-filled with **`_json_key`** (required by Google for service-account auth)
3. Create the snapshot using the full image reference
**`-docker.pkg.dev///:`**
#### GitHub Container Registry
Create a snapshot from images from GitHub Container Registry.
1. Go to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries),
2. Click and select the **GitHub** tab
2. Input the following fields:
- **GitHub Username**: the account with access to the image
- **Personal Access Token**: a [GitHub PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with **`read:packages`** scope (and **`write:packages`** / **`delete:packages`** for pushing or deleting)
- **Registry URL**: auto-filled with **`ghcr.io`** and not shown in the form
3. Create the snapshot using the full image reference
**`ghcr.io//:`**
#### Amazon ECR
Create a snapshot from images from Amazon Elastic Container Registry.
Daytona pulls private ECR images via cross-account IAM role assumption. You create a role in your AWS account that trusts Daytona's broker principal, and Daytona assumes it on every pull to fetch a short-lived ECR token.
- **Daytona Broker ARN**
The IAM principal Daytona uses to assume into your role. Self-hosted: substitute the IAM role your API pods assume (e.g. via IRSA).
`arn:aws:iam::967657494466:role/DaytonaEcrCredentialBroker`
- **External ID**
Your Daytona organization ID, visible in the dashboard URL (`/dashboard//...`) and on your organization settings page.
1. Create an IAM role in your AWS account
- **Trust policy**
```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::967657494466:role/DaytonaEcrCredentialBroker" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": ""
}
}
}]
}
```
- **Permissions policy (read-only on ECR)**
```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "*"
}]
}
```
2. Go to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries)
3. Click and select the **Amazon ECR** tab
4. Input the following fields:
- **Registry URL**: **`.dkr.ecr..amazonaws.com`**
- **Role ARN**: the role you created in step 1
Password is not used for ECR. Daytona resolves credentials server-side by assuming the role you created in step 1, using your organization ID as the **`AssumeRole ExternalId`**.
5. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
6. Click
7. Enter the snapshot **`name`** and the full **`image`** reference
**`.dkr.ecr..amazonaws.com//:`**
8. (Optional) Harden the trust policy
Daytona sends a `daytona--pull` session name on every AssumeRole call. You can require it in your trust policy for CloudTrail audit visibility. Add inside `Condition`:
```json
"StringLike": {
"sts:RoleSessionName": "daytona--*"
}
```
## Snapshots from local images
Create a snapshot from local images or from local Dockerfiles.
Daytona expects the local image to be built for AMD64 architecture. Therefore, the `--platform=linux/amd64` flag is required when building the Docker image if your machine is running on a different architecture.
1. Ensure the image and tag you want to use is available
```bash
docker images
```
2. Create a snapshot and push it to Daytona:
```bash
daytona snapshot push custom-alpine:3.21 --name alpine-minimal
```
Alternatively, use the `--dockerfile` flag under `create` to pass the path to the Dockerfile you want to use and Daytona will build the snapshot for you. The `COPY`/`ADD` commands will be automatically parsed and added to the context. To manually add files to the context, use the `--context` flag.
```bash
daytona snapshot create my-awesome-snapshot --dockerfile ./Dockerfile
```
:::note
Dockerfile builds are not supported for [VM snapshots](#vm-snapshots). For Linux VM snapshots, push the built image to a registry and create the snapshot from the image reference.
:::
## Get snapshot
Get a snapshot by name.
```python
daytona.snapshot.get("my-awesome-snapshot")
```
```typescript
await daytona.snapshot.get('my-awesome-snapshot')
```
```ruby
daytona.snapshot.get('my-awesome-snapshot')
```
```go
_, err := client.Snapshot.Get(ctx, "my-awesome-snapshot")
```
```java
daytona.snapshot().get("my-awesome-snapshot");
```
```bash
curl https://app.daytona.io/api/snapshots/my-awesome-snapshot \
--header 'Authorization: Bearer YOUR_SECRET_TOKEN'
```
## List snapshots
List snapshots and view their details.
```python
daytona.snapshot.list(page=2, limit=10)
```
```typescript
await daytona.snapshot.list(2, 10)
```
```ruby
daytona.snapshot.list(page: 2, limit: 10)
```
```go
page, limit := 2, 10
_, err := client.Snapshot.List(ctx, &page, &limit)
```
```java
daytona.snapshot().list(2, 10);
```
```bash
# List snapshots with pagination
daytona snapshot list --page 2 --limit 10
```
```bash
curl 'https://app.daytona.io/api/snapshots?page=2&limit=10' \
--header 'Authorization: Bearer YOUR_SECRET_TOKEN'
```
## Activate snapshots
Activate an inactive snapshot.
Snapshots automatically become inactive after 2 weeks of not being used.
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click the three dots at the end of the row for the snapshot you want to activate
3. Click
```python
daytona.snapshot.activate("my-awesome-snapshot")
```
```typescript
await daytona.snapshot.activate("my-awesome-snapshot")
```
```ruby
daytona.snapshot.activate('my-awesome-snapshot')
```
```bash
curl https://app.daytona.io/api/snapshots/my-inactive-snapshot/activate \
--request POST \
--header 'Authorization: Bearer YOUR_SECRET_TOKEN'
```
## Deactivate snapshots
Deactivate a snapshot.
Deactivated snapshots are not available for new sandboxes. Deactivating a snapshot also pauses top-ups of its [warm pools](https://www.daytona.io/docs/en/warm-pools.md); the pool reports the reason in its `errorReason` field.
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click the three dots at the end of the row for the snapshot you want to deactivate
3. Click
## Delete snapshots
Delete a snapshot.
Deleted snapshots cannot be recovered. Deleting a snapshot also deletes its [warm pools](https://www.daytona.io/docs/en/warm-pools.md) and destroys their unclaimed warm sandboxes.
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click the three dots at the end of the row for the snapshot you want to delete
3. Click
```python
daytona.snapshot.delete(daytona.snapshot.get("my-awesome-snapshot"))
```
```typescript
await daytona.snapshot.delete(await daytona.snapshot.get("my-awesome-snapshot"))
```
```ruby
daytona.snapshot.delete(daytona.snapshot.get('my-awesome-snapshot'))
```
```go
snapshot, err := client.Snapshot.Get(ctx, "my-awesome-snapshot")
err = client.Snapshot.Delete(ctx, snapshot)
```
```java
daytona.snapshot().delete(daytona.snapshot().get("my-awesome-snapshot").getId());
```
```bash
daytona snapshot delete my-awesome-snapshot
```
```bash
curl https://app.daytona.io/api/snapshots/my-awesome-snapshot \
--request DELETE \
--header 'Authorization: Bearer YOUR_SECRET_TOKEN'
```
## Snapshot lifecycle
A snapshot can have several different states. Each state reflects the snapshot's current status.
| **State** | **Description** |
| ------------ | ------------------------------------------------------------------------------------------------------- |
| Pending | The snapshot creation has been requested. |
| Building | The snapshot is being built. |
| Pulling | The snapshot image is being pulled from a registry. |
| Snapshotting | The snapshot is being created from a sandbox. |
| Active | The snapshot is ready to use for creating sandboxes. |
| Inactive | The snapshot is deactivated; must be explicitly [**activated**](#activate-snapshots) before use. |
| Error | The snapshot creation failed. |
| Build Failed | The snapshot build process failed. |
| Removing | The snapshot is being deleted. |
##### State transitions
A snapshot can transition between states in response to various actions. The following table lists the initial state, target state, and trigger for the transition.
| **Initial state** | **Target state** | **Trigger** |
| ----------------- | ---------------- | -------------------------------------------------------------------------------------------------- |
| Pending | Building | A declarative image build starts. |
| Pending | Pulling | The snapshot image pull starts. |
| Pending | Error | Snapshot processing fails. |
| Pending | Removing | A delete is requested. |
| Building | Active | The build finishes and the snapshot is ready. |
| Building | Build Failed | The image build is rejected. |
| Building | Error | The build fails or times out. |
| Building | Removing | A delete is requested. |
| Pulling | Active | The image pull finishes and the snapshot is ready. |
| Pulling | Error | The image pull fails or times out. |
| Pulling | Removing | A delete is requested. |
| Snapshotting | Active | The snapshot from a sandbox finishes and is ready. |
| Snapshotting | Error | The snapshot from a sandbox fails or times out. |
| Snapshotting | Removing | A delete is requested. |
| Active | Inactive | A deactivate is requested, the organization is suspended, or the deactivation timeout is exceeded. |
| Active | Removing | A delete is requested. |
| Inactive | Pending | An activate is requested. |
| Inactive | Removing | A delete is requested. |
| Error | Removing | A delete is requested. |
| Build Failed | Removing | A delete is requested. |
## Run Docker in a sandbox
Sandboxes can run Docker containers inside them (**Docker-in-Docker**), enabling you to build, test, and deploy containerized applications.
Agents can interact with these services since they run within the same sandbox environment, providing better isolation and security compared to external service dependencies.
- Run databases (PostgreSQL, Redis, MySQL) and other services
- Build and test containerized applications
- Deploy microservices and their dependencies
- Create isolated development environments with full container orchestration
:::note
Docker-in-Docker sandboxes require additional resources due to the Docker daemon overhead. Consider allocating at least 2 vCPU and 4GiB of memory for optimal performance.
:::
##### Create a Docker-in-Docker snapshot
Daytona provides an option to create a snapshot with Docker support using pre-built Docker-in-Docker images as a base or by manually installing Docker in a custom image.
###### Using pre-built images
The following base images are widely used for creating Docker-in-Docker snapshots or can be used as a base for a custom Dockerfile:
- **`docker:28.3.3-dind`**: official Docker-in-Docker image (Alpine-based, lightweight)
- **`docker:28.3.3-dind-rootless`**: rootless Docker-in-Docker for enhanced security
- **`docker:28.3.2-dind-alpine3.22`**: Docker-in-Docker image with Alpine 3.22
**Manual installation**
Alternatively, install Docker manually in a custom Dockerfile:
```dockerfile
FROM ubuntu:22.04
# Install Docker using the official install script
RUN curl -fsSL https://get.docker.com | VERSION=28.3.3 sh -
```
##### Run Docker Compose in a sandbox
Define and run multi-container applications. With Docker-in-Docker enabled in a Daytona sandbox, you can use Docker Compose to orchestrate services like databases, caches, and application containers.
1. Create a Docker-in-Docker snapshot with one of the [pre-built images](#using-pre-built-images)
2. Run Docker Compose services inside a sandbox
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
# Initialize the Daytona client
daytona = Daytona()
# Create a sandbox from a Docker-in-Docker snapshot
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot='docker-dind'))
# Create a docker-compose.yml file
compose_content = '''
services:
web:
image: nginx:alpine
ports:
- "8080:80"
'''
sandbox.fs.upload_file(compose_content.encode(), 'docker-compose.yml')
# Start Docker Compose services
result = sandbox.process.exec('docker compose -p demo up -d')
print(result.result)
# Check running services
result = sandbox.process.exec('docker compose -p demo ps')
print(result.result)
# Clean up
sandbox.process.exec('docker compose -p demo down')
```
```typescript
import { Daytona } from '@daytona/sdk'
// Initialize the Daytona client
const daytona = new Daytona()
// Create a sandbox from a Docker-in-Docker snapshot
const sandbox = await daytona.create({ snapshot: 'docker-dind' })
// Create a docker-compose.yml file
const composeContent = `
services:
web:
image: nginx:alpine
ports:
- "8080:80"
`
await sandbox.fs.uploadFile(Buffer.from(composeContent), 'docker-compose.yml')
// Start Docker Compose services
let result = await sandbox.process.executeCommand('docker compose -p demo up -d')
console.log(result.result)
// Check running services
result = await sandbox.process.executeCommand('docker compose -p demo ps')
console.log(result.result)
// Clean up
await sandbox.process.executeCommand('docker compose -p demo down')
```
```ruby
require 'daytona'
# Initialize the Daytona client
daytona = Daytona::Daytona.new
# Create a sandbox from a Docker-in-Docker snapshot
sandbox = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(snapshot: 'docker-dind'))
# Create a docker-compose.yml file
compose_content = <<~YAML
services:
web:
image: nginx:alpine
ports:
- "8080:80"
YAML
sandbox.fs.upload_file(compose_content, 'docker-compose.yml')
# Start Docker Compose services
result = sandbox.process.exec(command: 'docker compose -p demo up -d')
puts result.result
# Check running services
result = sandbox.process.exec(command: 'docker compose -p demo ps')
puts result.result
# Clean up
sandbox.process.exec(command: 'docker compose -p demo down')
```
```go
package main
import (
"context"
"fmt"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
ctx := context.Background()
// Initialize the Daytona client
client, _ := daytona.NewClient()
// Create a sandbox from a Docker-in-Docker snapshot
sandbox, _ := client.Create(ctx, types.SnapshotParams{
Snapshot: "docker-dind",
})
// Create a docker-compose.yml file
composeContent := `
services:
web:
image: nginx:alpine
ports:
- "8080:80"
`
sandbox.FileSystem.UploadFile(ctx, []byte(composeContent), "docker-compose.yml")
// Start Docker Compose services
result, _ := sandbox.Process.ExecuteCommand(ctx, "docker compose -p demo up -d")
fmt.Println(result.Result)
// Check running services
result, _ = sandbox.Process.ExecuteCommand(ctx, "docker compose -p demo ps")
fmt.Println(result.Result)
// Clean up
sandbox.Process.ExecuteCommand(ctx, "docker compose -p demo down")
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
import java.nio.charset.StandardCharsets;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
// Create a sandbox from a Docker-in-Docker snapshot
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("docker-dind");
Sandbox sandbox = daytona.create(params);
// Create a docker-compose.yml file
String composeContent = """
services:
web:
image: nginx:alpine
ports:
- "8080:80"
""";
sandbox.fs.uploadFile(composeContent.getBytes(StandardCharsets.UTF_8), "docker-compose.yml");
// Start Docker Compose services
ExecuteResponse result = sandbox.getProcess().executeCommand("docker compose -p demo up -d");
System.out.println(result.getResult());
// Check running services
result = sandbox.getProcess().executeCommand("docker compose -p demo ps");
System.out.println(result.getResult());
// Clean up
sandbox.getProcess().executeCommand("docker compose -p demo down");
}
}
}
```
## Run Kubernetes in a sandbox
Sandboxes can run a Kubernetes cluster inside the sandbox. Kubernetes runs entirely inside the sandbox and is removed when the sandbox is deleted, keeping environments secure and reproducible.
1. Create a sandbox
2. Install and start a k3s cluster inside the sandbox
```python
from daytona import Daytona, SessionExecuteRequest
import time
# Initialize the Daytona client
daytona = Daytona()
# Create the sandbox instance
sandbox = daytona.create()
# Run the k3s installation script
response = sandbox.process.exec('curl -sfL https://get.k3s.io | sh -')
# Run k3s
session_name = 'k3s-server'
sandbox.process.create_session(session_name)
sandbox.process.execute_session_command(
session_name,
SessionExecuteRequest(
command='sudo /usr/local/bin/k3s server',
run_async=True,
),
)
# Give time to k3s to fully start
time.sleep(30)
# Get all pods
pods = sandbox.process.exec('sudo /usr/local/bin/kubectl get pod -A')
print(pods.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
import { setTimeout } from 'timers/promises'
// Initialize the Daytona client
const daytona = new Daytona()
// Create the sandbox instance
const sandbox = await daytona.create()
// Run the k3s installation script
const response = await sandbox.process.executeCommand(
'curl -sfL https://get.k3s.io | sh -'
)
// Run k3s
const sessionName = 'k3s-server'
await sandbox.process.createSession(sessionName)
const k3s = await sandbox.process.executeSessionCommand(sessionName, {
command: 'sudo /usr/local/bin/k3s server',
async: true,
})
// Give time to k3s to fully start
await setTimeout(30000)
// Get all pods
const pods = await sandbox.process.executeCommand(
'sudo /usr/local/bin/kubectl get pod -A'
)
console.log(pods.result)
```
```ruby
require 'daytona'
# Initialize the Daytona client
daytona = Daytona::Daytona.new
# Create the sandbox instance
sandbox = daytona.create
# Run the k3s installation script
response = sandbox.process.exec(command: 'curl -sfL https://get.k3s.io | sh -')
# Run k3s
session_name = 'k3s-server'
sandbox.process.create_session(session_name)
sandbox.process.execute_session_command(
session_id: session_name,
req: Daytona::SessionExecuteRequest.new(
command: 'sudo /usr/local/bin/k3s server',
run_async: true
)
)
# Give time to k3s to fully start
sleep 30
# Get all pods
pods = sandbox.process.exec(command: 'sudo /usr/local/bin/kubectl get pod -A')
puts pods.result
```
```go
package main
import (
"context"
"fmt"
"time"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
ctx := context.Background()
// Initialize the Daytona client
client, _ := daytona.NewClient()
// Create the sandbox instance
sandbox, _ := client.Create(ctx, types.SnapshotParams{})
// Run the k3s installation script
_, _ = sandbox.Process.ExecuteCommand(ctx, "curl -sfL https://get.k3s.io | sh -")
// Run k3s
sessionName := "k3s-server"
_ = sandbox.Process.CreateSession(ctx, sessionName)
_, _ = sandbox.Process.ExecuteSessionCommand(
ctx, sessionName, "sudo /usr/local/bin/k3s server", true, false,
)
// Give time to k3s to fully start
time.Sleep(30 * time.Second)
// Get all pods
pods, _ := sandbox.Process.ExecuteCommand(ctx, "sudo /usr/local/bin/kubectl get pod -A")
fmt.Println(pods.Result)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.ExecuteResponse;
import io.daytona.sdk.model.SessionExecuteRequest;
public class App {
public static void main(String[] args) throws InterruptedException {
try (Daytona daytona = new Daytona()) {
// Create the sandbox instance
Sandbox sandbox = daytona.create();
// Run the k3s installation script
sandbox.getProcess().executeCommand("curl -sfL https://get.k3s.io | sh -");
// Run k3s
String sessionName = "k3s-server";
sandbox.getProcess().createSession(sessionName);
sandbox.getProcess().executeSessionCommand(
sessionName,
new SessionExecuteRequest("sudo /usr/local/bin/k3s server", true)
);
// Give time to k3s to fully start
Thread.sleep(30000);
// Get all pods
ExecuteResponse pods = sandbox.getProcess().executeCommand(
"sudo /usr/local/bin/kubectl get pod -A"
);
System.out.println(pods.getResult());
}
}
}
```
# Warm Pools
Warm pools keep a configured number of pre-created, running sandboxes built from a [snapshot](https://www.daytona.io/docs/en/snapshots.md) in a specific [region](https://www.daytona.io/docs/en/regions.md). When you create a sandbox that matches a pool, Daytona hands over a warm sandbox instead of provisioning a new one, so the sandbox is available instantly.
A sandbox create request claims a warm sandbox when all of the following match the pool:
- The same snapshot
- The same target region
- The snapshot's default resources (CPU, memory, and disk)
- The default OS user (`daytona`)
- No custom environment variables, volumes, or secrets
When a request claims a warm sandbox, Daytona applies the name, labels, auto-stop, auto-pause, auto-archive, and auto-delete intervals, and network settings from the create request.
:::note
Warm pools must be enabled for your organization. Contact [support@daytona.io](mailto:support@daytona.io).
:::
## Replenishment
Daytona replenishes warm pools automatically. Each time a request claims a warm sandbox, Daytona creates a new warm sandbox in the background to bring the pool back to its configured size. No action is required on your side: there is no manual trigger, and you do not need to recreate or resize the pool after sandboxes are claimed.
## Limits
An organization can have one warm pool per snapshot per region. Warm pools are bounded by the total [resources](https://www.daytona.io/docs/en/limits.md#resources) available to your organization. Warm sandboxes count against your organization quota in the same way as regular sandboxes, so the pool size multiplied by the snapshot's resources must fit within your region quota alongside your other running sandboxes. If the pool cannot be filled within the quota, the `errorReason` field on the pool explains why.
## Create a warm pool
Create a warm pool for a snapshot.
The snapshot must be active and available in the target region. Creating a second pool for the same snapshot and region returns a `409` error.
| **Parameter** | **Required** | **Description** |
| -------------- | ------------ | ------------------------------------------------------------------------------------- |
| **`snapshot`** | Yes | Snapshot ID or name to keep warm sandboxes for |
| **`pool`** | Yes | Number of warm sandboxes to keep ready; minimum **`1`**, capped by the organization quota |
| **`target`** | No | Target region for the warm pool; defaults to the organization default region |
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click the snapshot you want to create a warm pool for
3. In the **Warm Pool** section, select a region and enter the **Pool size**
4. Click
```python
from daytona import Daytona
daytona = Daytona()
pool = daytona.warm_pool.create("my-snapshot", pool=3, target="us")
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const pool = await daytona.warmPool.create({
snapshot: 'my-snapshot',
pool: 3,
target: 'us',
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
pool = daytona.warm_pool.create('my-snapshot', 3, target: 'us')
```
```go
package main
import (
"context"
"log"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
func main() {
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
target := "us"
pool, err := client.WarmPool.Create(context.Background(), &types.CreateWarmPoolParams{
Snapshot: "my-snapshot",
Pool: 3,
Target: &target,
})
if err != nil {
log.Fatal(err)
}
_ = pool
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.model.WarmPool;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
WarmPool pool = daytona.warmPool().create("my-snapshot", 3, "us");
}
}
}
```
```bash
curl 'https://app.daytona.io/api/warm-pools' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "my-snapshot",
"pool": 3,
"target": "us"
}'
```
## List warm pools
List the warm pools in your organization. Each pool reports the desired size (`pool`) and the number of warm sandboxes currently ready (`currentSize`). If Daytona cannot fill the pool, for example because the snapshot is no longer active, the `errorReason` field explains why.
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click a snapshot to see its warm pools per region in the **Warm Pool** section
```python
for pool in daytona.warm_pool.list():
print(f"{pool.snapshot}: {pool.current_size}/{pool.pool} ready")
```
```typescript
const pools = await daytona.warmPool.list()
for (const pool of pools) {
console.log(`${pool.snapshot}: ${pool.currentSize}/${pool.pool} ready`)
}
```
```ruby
daytona.warm_pool.list.each do |pool|
puts "#{pool.snapshot}: #{pool.current_size}/#{pool.pool} ready"
end
```
```go
pools, err := client.WarmPool.List(ctx)
if err != nil {
log.Fatal(err)
}
for _, pool := range pools {
fmt.Printf("%s: %d/%d ready\n", pool.Snapshot, pool.CurrentSize, pool.Pool)
}
```
```java
for (WarmPool pool : daytona.warmPool().list()) {
System.out.println(pool.getSnapshot() + ": "
+ pool.getCurrentSize() + "/" + pool.getPool() + " ready");
}
```
```bash
curl 'https://app.daytona.io/api/warm-pools' \
--header 'Authorization: Bearer YOUR_API_KEY'
```
## Resize a warm pool
Resize a warm pool to change the desired number of warm sandboxes. Daytona converges the pool to the new size in the background: it creates warm sandboxes when the pool is below the target and destroys the newest unclaimed ones when it is above. Setting `pool` to `0` drains the pool without deleting its configuration.
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click the snapshot to resize the warm pool for
3. In the **Warm Pool** section, click
4. Enter the new **Pool size** and click
```python
daytona.warm_pool.update(pool.id, pool=5)
```
```typescript
await daytona.warmPool.update(pool.id, { pool: 5 })
```
```ruby
daytona.warm_pool.update(pool.id, 5)
```
```go
updated, err := client.WarmPool.Update(ctx, pool.ID, 5)
if err != nil {
log.Fatal(err)
}
_ = updated
```
```java
daytona.warmPool().update(pool.getId(), 5);
```
```bash
curl 'https://app.daytona.io/api/warm-pools/{warmPoolId}' \
--request PATCH \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"pool": 5
}'
```
## Delete a warm pool
Delete a warm pool and destroy all of its unclaimed warm sandboxes. Sandboxes already claimed from the pool are not affected. Deleting a snapshot also deletes its warm pools.
1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click the snapshot to delete the warm pool for
3. In the **Warm Pool** section, click the trash icon
4. Click to confirm
```python
daytona.warm_pool.delete(pool.id)
```
```typescript
await daytona.warmPool.delete(pool.id)
```
```ruby
daytona.warm_pool.delete(pool.id)
```
```go
err := client.WarmPool.Delete(ctx, pool.ID)
if err != nil {
log.Fatal(err)
}
```
```java
daytona.warmPool().delete(pool.getId());
```
```bash
curl 'https://app.daytona.io/api/warm-pools/{warmPoolId}' \
--request DELETE \
--header 'Authorization: Bearer YOUR_API_KEY'
```
## List warm sandboxes
Unclaimed warm sandboxes are excluded from sandbox listings by default. Set the `includeWarm` query parameter to include them. While a sandbox waits in a pool, its `warmPoolId` field holds the id of the pool; the field is cleared when the sandbox is claimed.
1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Enable the **Warm pool** filter; warm sandboxes show a **Warm** badge next to their name
```bash
curl 'https://app.daytona.io/api/sandbox?includeWarm=true' \
--header 'Authorization: Bearer YOUR_API_KEY'
```
# Declarative Builder
Declarative Builder provides a powerful, code-first approach to defining dependencies for Daytona sandboxes. Instead of importing images from a container registry, you can programmatically define them using the Daytona SDK.
The declarative builder system supports two primary workflows:
- [**Declarative images**](#build-declarative-images): build images on demand when creating sandboxes
- [**Pre-built snapshots**](#create-pre-built-snapshots): create and register ready-to-use [snapshots](https://www.daytona.io/docs/snapshots.md)
:::note
Declarative image and Dockerfile builds are supported for container and GPU sandboxes only. [VM snapshots](https://www.daytona.io/docs/en/snapshots.md#vm-snapshots) (Linux VM and Windows) cannot be built from a Dockerfile. For a custom Linux VM image, push the image to a public or [private registry](https://www.daytona.io/docs/en/snapshots.md#snapshots-from-private-registries) and create the snapshot from the image reference, or [create a snapshot from a sandbox](https://www.daytona.io/docs/en/snapshots.md#create-snapshot-from-sandbox).
:::
## Build declarative images
Create a declarative image by defining the dependencies for the sandbox.
Declarative images are cached for 24 hours, and are automatically reused when running the same script. Thus, subsequent runs on the same runner will be almost instantaneous.
Create a container sandbox from a declarative image.
```python
from daytona import Image, CreateSandboxFromImageParams
# Define a declarative image with python packages
declarative_image = (
Image.debian_slim("3.12")
.pip_install(["requests", "pytest"])
.workdir("/home/daytona")
)
# Create a new sandbox with the declarative image and stream the build logs
sandbox = daytona.create(
CreateSandboxFromImageParams(image=declarative_image),
timeout=0,
on_snapshot_create_logs=print,
)
```
```typescript
import { Image } from '@daytona/sdk'
// Define a declarative image with python packages
const declarativeImage = Image.debianSlim('3.12')
.pipInstall(['requests', 'pytest'])
.workdir('/home/daytona')
// Create a new sandbox with the declarative image and stream the build logs
const sandbox = await daytona.create(
{
image: declarativeImage,
},
{
timeout: 0,
onSnapshotCreateLogs: console.log,
}
)
```
```ruby
# Define a simple declarative image with Python packages
declarative_image = Daytona::Image
.debian_slim('3.12')
.pip_install(['requests', 'pytest'])
.workdir('/home/daytona')
# Create a new Sandbox with the declarative image and stream the build logs
sandbox = daytona.create(
Daytona::CreateSandboxFromImageParams.new(image: declarative_image),
on_snapshot_create_logs: proc { |chunk| puts chunk }
)
```
```go
// Define a declarative image with python packages
version := "3.12"
declarativeImage := daytona.DebianSlim(&version).
PipInstall([]string{"requests", "pytest"}).
Workdir("/home/daytona")
// Create a new sandbox with the declarative image and stream the build logs
logChan := make(chan string)
go func() {
for log := range logChan {
fmt.Print(log)
}
}()
sandbox, err := client.Create(ctx, types.ImageParams{
Image: declarativeImage,
}, options.WithTimeout(0), options.WithLogChannel(logChan))
if err != nil {
// handle error
}
```
```java
// Define a declarative image with python packages
Image declarativeImage = Image.debianSlim("3.12")
.pipInstall("requests", "pytest")
.workdir("/home/daytona");
// Create a new sandbox with the declarative image and stream the build logs
CreateSandboxFromImageParams params = new CreateSandboxFromImageParams();
params.setImage(declarativeImage);
Sandbox sandbox = daytona.create(params, 0L, System.out::println);
```
Create a GPU sandbox from a declarative image.
```python
from daytona import Image, CreateSandboxFromImageParams, Resources
# Define a declarative image with python packages
declarative_image = (
Image.debian_slim("3.12")
.pip_install(["requests", "pytest"])
.workdir("/home/daytona")
)
# Create a GPU sandbox with the declarative image and stream the build logs
sandbox = daytona.create(
CreateSandboxFromImageParams(
image=declarative_image,
auto_delete_interval=0,
resources=Resources(gpu=1),
),
timeout=0,
on_snapshot_create_logs=print,
)
```
```typescript
import { Image } from '@daytona/sdk'
// Define a declarative image with python packages
const declarativeImage = Image.debianSlim('3.12')
.pipInstall(['requests', 'pytest'])
.workdir('/home/daytona')
// Create a GPU sandbox with the declarative image and stream the build logs
const sandbox = await daytona.create(
{
image: declarativeImage,
autoDeleteInterval: 0,
resources: { gpu: 1 },
},
{
timeout: 0,
onSnapshotCreateLogs: console.log,
}
)
```
```ruby
# Define a simple declarative image with Python packages
declarative_image = Daytona::Image
.debian_slim('3.12')
.pip_install(['requests', 'pytest'])
.workdir('/home/daytona')
# Create a GPU Sandbox with the declarative image and stream the build logs
sandbox = daytona.create(
Daytona::CreateSandboxFromImageParams.new(
image: declarative_image,
auto_delete_interval: 0,
resources: Daytona::Resources.new(gpu: 1)
),
on_snapshot_create_logs: proc { |chunk| puts chunk }
)
```
```go
// Define a declarative image with python packages
version := "3.12"
declarativeImage := daytona.DebianSlim(&version).
PipInstall([]string{"requests", "pytest"}).
Workdir("/home/daytona")
// Create a GPU sandbox with the declarative image and stream the build logs
autoDelete := 0
logChan := make(chan string)
go func() {
for log := range logChan {
fmt.Print(log)
}
}()
sandbox, err := client.Create(ctx, types.ImageParams{
Image: declarativeImage,
SandboxBaseParams: types.SandboxBaseParams{
AutoDeleteInterval: &autoDelete,
},
Resources: &types.Resources{
GPU: 1,
},
}, options.WithTimeout(0), options.WithLogChannel(logChan))
if err != nil {
// handle error
}
```
```java
// Define a declarative image with python packages
Image declarativeImage = Image.debianSlim("3.12")
.pipInstall("requests", "pytest")
.workdir("/home/daytona");
// Create a GPU sandbox with the declarative image and stream the build logs
CreateSandboxFromImageParams params = new CreateSandboxFromImageParams();
params.setImage(declarativeImage);
params.setAutoDeleteInterval(0);
Resources resources = new Resources();
resources.setGpu(1);
params.setResources(resources);
Sandbox sandbox = daytona.create(params, 0L, System.out::println);
```
## Create pre-built snapshots
Create a pre-built snapshot by building a declarative image and registering it as a [snapshot](https://www.daytona.io/docs/en/snapshots.md).
1. Create a container snapshot from a declarative image
2. Create a sandbox from that snapshot
```python
from daytona import Image, CreateSnapshotParams, CreateSandboxFromSnapshotParams
# Define the declarative image for the snapshot
image = (
Image.debian_slim("3.12")
.pip_install(["numpy", "pandas"])
.workdir("/home/daytona")
)
# Create and register the snapshot, streaming the build logs
daytona.snapshot.create(
CreateSnapshotParams(name="my-snapshot", image=image),
on_logs=print,
)
# Create a new sandbox from the pre-built snapshot
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="my-snapshot"))
```
```typescript
import { Image } from '@daytona/sdk'
// Define the declarative image for the snapshot
const image = Image.debianSlim('3.12')
.pipInstall(['numpy', 'pandas'])
.workdir('/home/daytona')
// Create and register the snapshot, streaming the build logs
await daytona.snapshot.create(
{
name: 'my-snapshot',
image,
},
{
onLogs: console.log,
}
)
// Create a new sandbox from the pre-built snapshot
const sandbox = await daytona.create({ snapshot: 'my-snapshot' })
```
```ruby
# Define the declarative image for the snapshot
image = Daytona::Image
.debian_slim('3.12')
.pip_install(['numpy', 'pandas'])
.workdir('/home/daytona')
# Create and register the snapshot, streaming the build logs
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(name: 'my-snapshot', image: image),
on_logs: proc { |chunk| print chunk }
)
# Create a new sandbox from the pre-built snapshot
sandbox = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(snapshot: 'my-snapshot'))
```
```go
// Define the declarative image for the snapshot
version := "3.12"
image := daytona.DebianSlim(&version).
PipInstall([]string{"numpy", "pandas"}).
Workdir("/home/daytona")
// Create and register the snapshot, streaming the build logs
snapshot, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "my-snapshot",
Image: image,
})
if err != nil {
// handle error
}
for log := range logChan {
fmt.Print(log)
}
// Create a new sandbox from the pre-built snapshot
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: snapshot.Name,
})
if err != nil {
// handle error
}
```
```java
// Define the declarative image for the snapshot
Image image = Image.debianSlim("3.12")
.pipInstall("numpy", "pandas")
.workdir("/home/daytona");
// Create and register the snapshot, streaming the build logs
Snapshot snapshot = daytona.snapshot().create("my-snapshot", image, System.out::println);
// Create a new sandbox from the pre-built snapshot
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my-snapshot");
Sandbox sandbox = daytona.create(params);
```
1. Create a GPU snapshot from a declarative image
2. Create a sandbox from that snapshot
```python
from daytona import Image, CreateSnapshotParams, CreateSandboxFromSnapshotParams, Resources
# Define the declarative image for the GPU snapshot
image = (
Image.debian_slim("3.12")
.pip_install(["numpy", "pandas"])
.workdir("/home/daytona")
)
# Create and register the GPU snapshot, streaming the build logs
daytona.snapshot.create(
CreateSnapshotParams(
name="my-gpu-snapshot",
image=image,
resources=Resources(gpu=1),
),
on_logs=print,
)
# Create a new GPU sandbox from the pre-built snapshot
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="my-gpu-snapshot",
auto_delete_interval=0,
)
)
```
```typescript
import { Image } from '@daytona/sdk'
// Define the declarative image for the GPU snapshot
const image = Image.debianSlim('3.12')
.pipInstall(['numpy', 'pandas'])
.workdir('/home/daytona')
// Create and register the GPU snapshot, streaming the build logs
await daytona.snapshot.create(
{
name: 'my-gpu-snapshot',
image,
resources: { gpu: 1 },
},
{
onLogs: console.log,
}
)
// Create a new GPU sandbox from the pre-built snapshot
const sandbox = await daytona.create({
snapshot: 'my-gpu-snapshot',
autoDeleteInterval: 0,
})
```
```ruby
# Define the declarative image for the GPU snapshot
image = Daytona::Image
.debian_slim('3.12')
.pip_install(['numpy', 'pandas'])
.workdir('/home/daytona')
# Create and register the GPU snapshot, streaming the build logs
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(
name: 'my-gpu-snapshot',
image: image,
resources: Daytona::Resources.new(gpu: 1)
),
on_logs: proc { |chunk| print chunk }
)
# Create a new GPU sandbox from the pre-built snapshot
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'my-gpu-snapshot',
auto_delete_interval: 0
)
)
```
```go
// Define the declarative image for the GPU snapshot
version := "3.12"
image := daytona.DebianSlim(&version).
PipInstall([]string{"numpy", "pandas"}).
Workdir("/home/daytona")
// Create and register the GPU snapshot, streaming the build logs
snapshot, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "my-gpu-snapshot",
Image: image,
Resources: &types.Resources{
GPU: 1,
},
})
if err != nil {
// handle error
}
for log := range logChan {
fmt.Print(log)
}
// Create a new GPU sandbox from the pre-built snapshot
autoDelete := 0
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: snapshot.Name,
SandboxBaseParams: types.SandboxBaseParams{
AutoDeleteInterval: &autoDelete,
},
})
if err != nil {
// handle error
}
```
```java
// Define the declarative image for the GPU snapshot
Image image = Image.debianSlim("3.12")
.pipInstall("numpy", "pandas")
.workdir("/home/daytona");
Resources resources = new Resources();
resources.setGpu(1);
// Create and register the GPU snapshot, streaming the build logs
Snapshot snapshot = daytona.snapshot().create(
"my-gpu-snapshot", image, resources, System.out::println);
// Create a new GPU sandbox from the pre-built snapshot
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my-gpu-snapshot");
params.setAutoDeleteInterval(0);
Sandbox sandbox = daytona.create(params);
```
## Image configuration
Daytona provides an option to define images programmatically. Chain the methods below to build a complete image definition in a single fluent call.
1. **Select a base image**
Start from any registry image with `Image.base()`, or use `Image.debian_slim()` for a Python-ready Debian image.
2. **Install Python packages**
Add packages with `pip_install()`, or install from `requirements.txt` or `pyproject.toml` using `pip_install_from_requirements()` and `pip_install_from_pyproject()`.
3. **Add files and directories**
Copy local files into the image with `add_local_file()` and `add_local_dir()`.
4. **Configure environment**
Set environment variables and the working directory with `env()` and `workdir()`.
5. **Install system packages**
Use `run_commands()` to install OS-level CLI tools and libraries not available through `pip`. Chain `apt-get update`, install, and cache cleanup with `&&` in a single command to minimize Docker layers.
6. **Add additional runtimes**
Install secondary language runtimes in a single chained `RUN` instruction. The example below adds Node.js 20 alongside Python.
7. **Set up a non-root user**
Run all installation steps as `root` first, then create the user, fix ownership of the working directory, and switch with the `USER` directive. Commands that write to system locations after switching users will fail with permission errors.
8. **Configure startup**
Set the container entrypoint and default command with `entrypoint()` and `cmd()`.
```python
from daytona import Image
image = (
# 1. Base image
Image.debian_slim("3.12")
# 2. Python packages
.pip_install(["requests", "pandas"])
# 3. Local files
.add_local_file("package.json", "/home/daytona/package.json")
.add_local_dir("src", "/home/daytona/src")
# 4. Environment
.env({"PROJECT_ROOT": "/home/daytona"})
.workdir("/home/daytona")
# 5. System packages
.run_commands(
"apt-get update "
"&& apt-get install -y --no-install-recommends git curl ffmpeg jq "
"&& rm -rf /var/lib/apt/lists/*"
)
# 6. Additional runtime
.run_commands(
"apt-get update "
"&& apt-get install -y --no-install-recommends curl ca-certificates "
"&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - "
"&& apt-get install -y nodejs "
"&& rm -rf /var/lib/apt/lists/*"
)
# 7. Non-root user
.run_commands(
"groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona",
"chown -R daytona:daytona /home/daytona",
)
.dockerfile_commands(["USER daytona"])
# 8. Startup
.entrypoint(["/bin/bash"])
.cmd(["/bin/bash"])
)
```
```typescript
import { Image } from '@daytona/sdk'
// 1. Base image
const image = Image.debianSlim('3.12')
// 2. Python packages
.pipInstall(['requests', 'pandas'])
// 3. Local files
.addLocalFile('package.json', '/home/daytona/package.json')
.addLocalDir('src', '/home/daytona/src')
// 4. Environment
.env({ PROJECT_ROOT: '/home/daytona' })
.workdir('/home/daytona')
// 5. System packages
.runCommands(
'apt-get update ' +
'&& apt-get install -y --no-install-recommends git curl ffmpeg jq ' +
'&& rm -rf /var/lib/apt/lists/*',
)
// 6. Additional runtime
.runCommands(
'apt-get update ' +
'&& apt-get install -y --no-install-recommends curl ca-certificates ' +
'&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - ' +
'&& apt-get install -y nodejs ' +
'&& rm -rf /var/lib/apt/lists/*',
)
// 7. Non-root user
.runCommands(
'groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona',
'chown -R daytona:daytona /home/daytona',
)
.dockerfileCommands(['USER daytona'])
// 8. Startup
.entrypoint(['/bin/bash'])
.cmd(['/bin/bash'])
```
```ruby
image = Daytona::Image
# 1. Base image
.debian_slim('3.12')
# 2. Python packages
.pip_install(['requests', 'pandas'])
# 3. Local files
.add_local_file('package.json', '/home/daytona/package.json')
.add_local_dir('src', '/home/daytona/src')
# 4. Environment
.env({ 'PROJECT_ROOT' => '/home/daytona' })
.workdir('/home/daytona')
# 5. System packages
.run_commands(
'apt-get update ' \
'&& apt-get install -y --no-install-recommends git curl ffmpeg jq ' \
'&& rm -rf /var/lib/apt/lists/*'
)
# 6. Additional runtime
.run_commands(
'apt-get update ' \
'&& apt-get install -y --no-install-recommends curl ca-certificates ' \
'&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - ' \
'&& apt-get install -y nodejs ' \
'&& rm -rf /var/lib/apt/lists/*'
)
# 7. Non-root user
.run_commands(
'groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona',
'chown -R daytona:daytona /home/daytona'
)
.dockerfile_commands(['USER daytona'])
# 8. Startup
.entrypoint(['/bin/bash'])
.cmd(['/bin/bash'])
```
```go
version := "3.12"
// 1. Base image
image := daytona.DebianSlim(&version).
// 2. Python packages
PipInstall([]string{"requests", "pandas"}).
// 3. Local files
AddLocalFile("package.json", "/home/daytona/package.json").
AddLocalDir("src", "/home/daytona/src").
// 4. Environment
Env("PROJECT_ROOT", "/home/daytona").
Workdir("/home/daytona").
// 5. System packages
AptGet([]string{"git", "curl", "ffmpeg", "jq"}).
// 6. Additional runtime
Run("apt-get update " +
"&& apt-get install -y --no-install-recommends curl ca-certificates " +
"&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - " +
"&& apt-get install -y nodejs " +
"&& rm -rf /var/lib/apt/lists/*").
// 7. Non-root user
Run("groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona").
Run("chown -R daytona:daytona /home/daytona").
User("daytona").
// 8. Startup
Entrypoint([]string{"/bin/bash"}).
Cmd([]string{"/bin/bash"})
```
```java
// 1. Base image
Image image = Image.debianSlim("3.12")
// 2. Python packages
.pipInstall("requests", "pandas")
// 3. Local files
.addLocalFile("package.json", "/home/daytona/package.json")
.addLocalDir("src", "/home/daytona/src")
// 4. Environment
.env(java.util.Map.of("PROJECT_ROOT", "/home/daytona"))
.workdir("/home/daytona")
// 5. System packages
.runCommands(
"apt-get update "
+ "&& apt-get install -y --no-install-recommends git curl ffmpeg jq "
+ "&& rm -rf /var/lib/apt/lists/*"
)
// 6. Additional runtime
.runCommands(
"apt-get update "
+ "&& apt-get install -y --no-install-recommends curl ca-certificates "
+ "&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - "
+ "&& apt-get install -y nodejs "
+ "&& rm -rf /var/lib/apt/lists/*"
)
// 7. Non-root user (the Java SDK has no method for a USER instruction)
.runCommands(
"groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona",
"chown -R daytona:daytona /home/daytona"
)
// 8. Startup
.entrypoint("/bin/bash")
.cmd("/bin/bash");
```
### Dockerfile integration
Integrate Dockerfiles and custom Dockerfile commands.
```python
from daytona import Image
# Add custom Dockerfile commands
image = Image.debian_slim("3.12").dockerfile_commands(["RUN echo 'Hello, world!'"])
# Use an existing Dockerfile
image = Image.from_dockerfile("Dockerfile")
# Extend an existing Dockerfile
image = Image.from_dockerfile("app/Dockerfile").pip_install(["numpy"])
```
```typescript
import { Image } from '@daytona/sdk'
// Add custom Dockerfile commands
let image = Image.debianSlim('3.12').dockerfileCommands(['RUN echo "Hello, world!"'])
// Use an existing Dockerfile
image = Image.fromDockerfile('Dockerfile')
// Extend an existing Dockerfile
image = Image.fromDockerfile('app/Dockerfile').pipInstall(['numpy'])
```
```ruby
# Add custom Dockerfile commands
image = Daytona::Image.debian_slim('3.12').dockerfile_commands(['RUN echo "Hello, world!"'])
# Use an existing Dockerfile
image = Daytona::Image.from_dockerfile('Dockerfile')
# Extend an existing Dockerfile
image = Daytona::Image.from_dockerfile('app/Dockerfile').pip_install(['numpy'])
```
```go
// Note: In Go, FromDockerfile takes the Dockerfile content as a string
content, err := os.ReadFile("Dockerfile")
if err != nil {
// handle error
}
image := daytona.FromDockerfile(string(content))
// Extend an existing Dockerfile with additional commands
content, err = os.ReadFile("app/Dockerfile")
if err != nil {
// handle error
}
image = daytona.FromDockerfile(string(content)).
PipInstall([]string{"numpy"})
```
# Volumes
Volumes are FUSE-based mounts that provide shared file access across Daytona sandboxes. They enable sandboxes to read from large files instantly - no need to upload files manually to each sandbox. Volume data is stored in an S3-compatible object store.
A sandbox reads and writes a mounted volume like any local directory, and the contents persist independently of the sandbox lifecycle. Use volumes to share datasets, model weights, build caches, or application state between sandboxes, scope per-user or per-tenant data with a `subpath`, and combine multiple volumes in the same sandbox at different mount paths.
- multiple volumes can be mounted to a single sandbox
- a single volume can be mounted to multiple sandboxes
## Create volumes
Create a volume.
For persistent per-user, per-tenant, or per-workspace storage, use one shared volume per use case, environment, or project (for example a volume for staging and another for production), and set a dedicated `subpath` when you create each sandbox. The sandbox sees only that prefix inside the volume; it cannot access sibling subpaths.
This is the default pattern we recommend because it:
- stays within the per-organization volume [limits](#pricing--limits)
- avoids mounting a separate volume for every user or sandbox
- continues to provide strong isolation at the mount boundary
1. Go to [Daytona Volumes ↗](https://app.daytona.io/dashboard/volumes)
2. Click
3. Enter the volume name
4. Click
```python
from daytona import Daytona
daytona = Daytona()
volume = daytona.volume.create("my-awesome-volume")
```
```typescript
import { Daytona } from "@daytona/sdk";
const daytona = new Daytona();
const volume = await daytona.volume.create("my-awesome-volume");
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
volume = daytona.volume.create("my-awesome-volume")
```
```go
package main
import (
"context"
"fmt"
"log"
"github.com/daytona/clients/sdk-go/pkg/daytona"
)
func main() {
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
volume, err := client.Volume.Create(context.Background(), "my-awesome-volume")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Volume ID: %s\n", volume.ID)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.model.Volume;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Volume volume = daytona.volume().create("my-awesome-volume");
}
}
}
```
```shell
daytona volume create my-awesome-volume
```
```bash
curl 'https://app.daytona.io/api/volumes' \
--request POST \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"name": "my-awesome-volume"
}'
```
## Wait for a volume to be ready
Wait until a newly created volume reaches the `ready` state before mounting it to a sandbox. Mounting a volume that is still provisioning returns an error.
```python
import time
from daytona import Daytona
daytona = Daytona()
volume = daytona.volume.create("my-awesome-volume")
while volume.state != "ready":
time.sleep(1)
volume = daytona.volume.get("my-awesome-volume")
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
let volume = await daytona.volume.create('my-awesome-volume')
while (volume.state !== 'ready') {
await new Promise((resolve) => setTimeout(resolve, 1000))
volume = await daytona.volume.get('my-awesome-volume')
}
```
```go
import (
"context"
"log"
"time"
"github.com/daytona/clients/sdk-go/pkg/daytona"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
volume, err := client.Volume.Create(ctx, "my-awesome-volume")
if err != nil {
log.Fatal(err)
}
volume, err = client.Volume.WaitForReady(ctx, volume, 2*time.Minute)
if err != nil {
log.Fatal(err)
}
```
## Mount volumes
Mount a volume to a sandbox at sandbox creation time.
For per-user or multi-tenant data, pass `subpath` so only the specified folder inside the volume is visible at `mount_path`. Mount the entire volume (omit `subpath`) when every sandbox that uses that volume should see the same tree. The volume must be in the `ready` state.
Volume mount paths must meet the following requirements:
- **Must be absolute paths**: mount paths must start with `/` (e.g., `/home/daytona/volume`)
- **Cannot be root directory**: cannot mount to `/` or `//`
- **No relative path components**: cannot contain `/../`, `/./`, or end with `/..` or `/.`
- **No consecutive slashes**: cannot contain multiple consecutive slashes like `//` (except at the beginning)
- **Cannot mount to system directories**: the following system directories are prohibited: `/proc`, `/sys`, `/dev`, `/boot`, `/etc`, `/bin`, `/sbin`, `/lib`, `/lib64`
When you set `subpath`, the value must meet the following requirements:
- **No leading slash**: subpaths are S3 key prefixes and must not start with `/`
- **No path traversal**: cannot contain `..`
- **No consecutive slashes**: cannot contain `//`
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount
daytona = Daytona()
# Create a new volume or get an existing one
volume = daytona.volume.get("my-awesome-volume", create=True)
mount_dir = "/home/daytona/volume"
# Recommended for per-user / per-tenant data: one volume, unique subpath per sandbox
params = CreateSandboxFromSnapshotParams(
language="python",
volumes=[VolumeMount(volume_id=volume.id, mount_path=mount_dir, subpath="users/alice")],
)
sandbox = daytona.create(params)
# Entire volume at mount path (omit subpath) when all sandboxes should share the same tree
params_full = CreateSandboxFromSnapshotParams(
language="python",
volumes=[VolumeMount(volume_id=volume.id, mount_path=mount_dir)],
)
sandbox_shared = daytona.create(params_full)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const volume = await daytona.volume.get('my-awesome-volume', true)
const mountDir = '/home/daytona/volume'
// Recommended for per-user / per-tenant data: one volume, unique subpath per sandbox
const sandbox = await daytona.create({
language: 'typescript',
volumes: [
{ volumeId: volume.id, mountPath: mountDir, subpath: 'users/alice' },
],
})
// Entire volume at mount path (omit subpath) when all sandboxes should share the same tree
const sandboxShared = await daytona.create({
language: 'typescript',
volumes: [{ volumeId: volume.id, mountPath: mountDir }],
})
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
volume = daytona.volume.get('my-awesome-volume', create: true)
mount_dir = '/home/daytona/volume'
# Recommended for per-user / per-tenant data: one volume, unique subpath per sandbox
params = Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::PYTHON,
volumes: [DaytonaApiClient::SandboxVolume.new(
volume_id: volume.id,
mount_path: mount_dir,
subpath: 'users/alice'
)]
)
sandbox = daytona.create(params)
# Entire volume at mount path (omit subpath) when all sandboxes should share the same tree
params_shared = Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::PYTHON,
volumes: [DaytonaApiClient::SandboxVolume.new(volume_id: volume.id, mount_path: mount_dir)]
)
sandbox_shared = daytona.create(params_shared)
```
```go
import (
"context"
"log"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
// Create a new volume or get an existing one
volume, err := client.Volume.Get(context.Background(), "my-awesome-volume")
if err != nil {
// If volume doesn't exist, create it
volume, err = client.Volume.Create(context.Background(), "my-awesome-volume")
if err != nil {
log.Fatal(err)
}
}
mountDir := "/home/daytona/volume"
// Recommended for per-user / per-tenant data: one volume, unique subpath per sandbox
subpath := "users/alice"
sandbox, err := client.Create(context.Background(), types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
Language: types.CodeLanguagePython,
Volumes: []types.VolumeMount{
{VolumeID: volume.ID, MountPath: mountDir, Subpath: &subpath},
},
},
})
if err != nil {
log.Fatal(err)
}
// Entire volume at mount path (omit Subpath) when all sandboxes should share the same tree
_, err = client.Create(context.Background(), types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
Language: types.CodeLanguagePython,
Volumes: []types.VolumeMount{
{VolumeID: volume.ID, MountPath: mountDir},
},
},
})
if err != nil {
log.Fatal(err)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.exception.DaytonaNotFoundException;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.Volume;
import io.daytona.sdk.model.VolumeMount;
import java.util.Collections;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Volume volume;
try {
volume = daytona.volume().getByName("my-awesome-volume");
} catch (DaytonaNotFoundException e) {
volume = daytona.volume().create("my-awesome-volume");
}
String mountDir = "/home/daytona/volume";
// io.daytona.sdk.model.VolumeMount has no subpath field; use the API for subpath mounts.
// Mount the entire volume at mountPath:
CreateSandboxFromSnapshotParams paramsFull = new CreateSandboxFromSnapshotParams();
paramsFull.setLanguage("python");
VolumeMount mountFull = new VolumeMount();
mountFull.setVolumeId(volume.getId());
mountFull.setMountPath(mountDir);
paramsFull.setVolumes(Collections.singletonList(mountFull));
Sandbox sandboxShared = daytona.create(paramsFull);
}
}
}
```
```shell
daytona volume create my-awesome-volume
daytona create --volume my-awesome-volume:/home/daytona/volume
```
The `--volume` flag accepts `VOLUME_ID_OR_NAME:MOUNT_PATH` only. For a **subpath** mount, use a [SDK](#mount-volumes) or the [API](#mount-volumes) and set `subpath` on the volume entry.
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"volumes": [
{
"volumeId": "",
"mountPath": "/home/daytona/volume",
"subpath": "users/alice"
}
]
}'
```
Omit `subpath` to mount the full volume at `mountPath`.
## Work with volumes
Read from and write to a volume just like any other directory in the sandbox file system. Files written to the volume persist beyond the lifecycle of any individual sandbox.
```python
# Write to a file in the mounted volume using the Sandbox file system API
sandbox.fs.upload_file(b"Hello from Daytona volume!", "/home/daytona/volume/example.txt")
# When you're done with the sandbox, you can remove it
# The volume will persist even after the sandbox is removed
sandbox.delete()
```
```typescript
// Write to a file in the mounted volume using the Sandbox file system API
await sandbox.fs.uploadFile(
Buffer.from('Hello from Daytona volume!'),
'/home/daytona/volume/example.txt'
)
// When you're done with the sandbox, you can remove it
// The volume will persist even after the sandbox is removed
await daytona.delete(sandbox)
```
```ruby
# Write to a file in the mounted volume using the Sandbox file system API
sandbox.fs.upload_file('Hello from Daytona volume!', '/home/daytona/volume/example.txt')
# When you're done with the sandbox, you can remove it
# The volume will persist even after the sandbox is removed
daytona.delete(sandbox)
```
```go
import (
"context"
"log"
)
// Write to a file in the mounted volume
err := sandbox.FileSystem.UploadFile(context.Background(), []byte("Hello from Daytona volume!"), "/home/daytona/volume/example.txt")
if err != nil {
log.Fatal(err)
}
// When you're done with the sandbox, you can remove it
// The volume will persist even after the sandbox is removed
err = sandbox.Delete(context.Background())
if err != nil {
log.Fatal(err)
}
```
```java
import java.nio.charset.StandardCharsets;
// Write to a file in the mounted volume using the Sandbox file system API
sandbox.fs.uploadFile(
"Hello from Daytona volume!".getBytes(StandardCharsets.UTF_8),
"/home/daytona/volume/example.txt");
// When you're done with the sandbox, you can remove it
// The volume will persist even after the sandbox is removed
sandbox.delete();
```
## Get a volume by name
Get a volume by its name.
```python
daytona.volume.get("my-awesome-volume", create=True)
```
```typescript
await daytona.volume.get('my-awesome-volume', true)
```
```ruby
daytona.volume.get('my-awesome-volume', create: true)
```
```go
volume, err := client.Volume.Get(ctx, "my-awesome-volume")
```
```java
daytona.volume().getByName("my-awesome-volume");
```
```shell
daytona volume get my-awesome-volume
```
```bash
curl 'https://app.daytona.io/api/volumes/by-name/my-awesome-volume' \
--header 'Authorization: Bearer '
```
## Get a volume by ID
Get a volume by its ID.
```bash
curl 'https://app.daytona.io/api/volumes/' \
--header 'Authorization: Bearer '
```
## List volumes
List all volumes.
```python
daytona.volume.list()
```
```typescript
await daytona.volume.list()
```
```ruby
daytona.volume.list
```
```go
volumes, err := client.Volume.List(ctx)
```
```java
daytona.volume().list();
```
```shell
daytona volume list
```
```bash
curl 'https://app.daytona.io/api/volumes' \
--header 'Authorization: Bearer '
```
## Delete volumes
Delete a volume. Deletion is asynchronous: the volume moves through `pending_delete` and `deleting` before it is removed. Deleted volumes cannot be recovered.
A volume can be deleted only when it is in the `ready` or `error` state and is not mounted by any active sandbox. Attempting to delete a volume that is still in use returns a `409` error.
1. Go to [Daytona Volumes ↗](https://app.daytona.io/dashboard/volumes)
2. Click next to the volume you want to delete
3. Confirm the deletion
```python
daytona.volume.delete(volume)
```
```typescript
await daytona.volume.delete(volume)
```
```ruby
daytona.volume.delete(volume)
```
```go
err := client.Volume.Delete(ctx, volume)
```
```java
daytona.volume().delete(volume.getId());
```
```shell
daytona volume delete
```
```bash
curl 'https://app.daytona.io/api/volumes/' \
--request DELETE \
--header 'Authorization: Bearer '
```
## Share data between sandboxes
Share data across sandboxes by mounting the same volume in each one.
A producer sandbox writes to the volume and is then deleted; a separately created consumer sandbox mounts the same volume by ID and reads the data. Volume contents persist independently of any individual sandbox.
Sandboxes that mount the same volume see writes immediately, but FUSE-backed volumes are not transactional. If two sandboxes write to the same path concurrently, the last write wins. Coordinate access in your application when ordering matters.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount
daytona = Daytona()
volume = daytona.volume.get("shared-data", create=True)
mount_dir = "/home/daytona/volume"
# Producer: write data into the volume, then delete the sandbox
producer = daytona.create(CreateSandboxFromSnapshotParams(
language="python",
volumes=[VolumeMount(volume_id=volume.id, mount_path=mount_dir)],
))
producer.fs.upload_file(b"shared payload", f"{mount_dir}/payload.bin")
producer.delete()
# Consumer: a separate sandbox mounts the same volume by ID and reads the data
consumer = daytona.create(CreateSandboxFromSnapshotParams(
language="python",
volumes=[VolumeMount(volume_id=volume.id, mount_path=mount_dir)],
))
data = consumer.fs.download_file(f"{mount_dir}/payload.bin")
print(data.decode())
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const volume = await daytona.volume.get('shared-data', true)
const mountDir = '/home/daytona/volume'
// Producer: write data into the volume, then delete the sandbox
const producer = await daytona.create({
language: 'typescript',
volumes: [{ volumeId: volume.id, mountPath: mountDir }],
})
await producer.fs.uploadFile(Buffer.from('shared payload'), `${mountDir}/payload.bin`)
await daytona.delete(producer)
// Consumer: a separate sandbox mounts the same volume by ID and reads the data
const consumer = await daytona.create({
language: 'typescript',
volumes: [{ volumeId: volume.id, mountPath: mountDir }],
})
const data = await consumer.fs.downloadFile(`${mountDir}/payload.bin`)
console.log(data.toString())
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
volume = daytona.volume.get('shared-data', create: true)
mount_dir = '/home/daytona/volume'
mount = DaytonaApiClient::SandboxVolume.new(volume_id: volume.id, mount_path: mount_dir)
# Producer: write data into the volume, then delete the sandbox
producer = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::PYTHON,
volumes: [mount]
))
producer.fs.upload_file('shared payload', "#{mount_dir}/payload.bin")
daytona.delete(producer)
# Consumer: a separate sandbox mounts the same volume by ID and reads the data
consumer = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::PYTHON,
volumes: [mount]
))
data = consumer.fs.download_file("#{mount_dir}/payload.bin")
puts data
```
```go
import (
"context"
"fmt"
"log"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
volume, err := client.Volume.Get(ctx, "shared-data")
if err != nil {
volume, err = client.Volume.Create(ctx, "shared-data")
if err != nil {
log.Fatal(err)
}
}
mountDir := "/home/daytona/volume"
mount := types.VolumeMount{VolumeID: volume.ID, MountPath: mountDir}
// Producer: write data into the volume, then delete the sandbox
producer, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
Language: types.CodeLanguagePython,
Volumes: []types.VolumeMount{mount},
},
})
if err != nil {
log.Fatal(err)
}
if err := producer.FileSystem.UploadFile(ctx, []byte("shared payload"), mountDir+"/payload.bin"); err != nil {
log.Fatal(err)
}
if err := producer.Delete(ctx); err != nil {
log.Fatal(err)
}
// Consumer: a separate sandbox mounts the same volume by ID and reads the data
consumer, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
Language: types.CodeLanguagePython,
Volumes: []types.VolumeMount{mount},
},
})
if err != nil {
log.Fatal(err)
}
data, err := consumer.FileSystem.DownloadFile(ctx, mountDir+"/payload.bin", nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.exception.DaytonaNotFoundException;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.Volume;
import io.daytona.sdk.model.VolumeMount;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Volume volume;
try {
volume = daytona.volume().getByName("shared-data");
} catch (DaytonaNotFoundException e) {
volume = daytona.volume().create("shared-data");
}
String mountDir = "/home/daytona/volume";
VolumeMount mount = new VolumeMount();
mount.setVolumeId(volume.getId());
mount.setMountPath(mountDir);
// Producer: write data into the volume, then delete the sandbox
CreateSandboxFromSnapshotParams producerParams = new CreateSandboxFromSnapshotParams();
producerParams.setLanguage("python");
producerParams.setVolumes(Collections.singletonList(mount));
Sandbox producer = daytona.create(producerParams);
producer.fs.uploadFile(
"shared payload".getBytes(StandardCharsets.UTF_8),
mountDir + "/payload.bin");
producer.delete();
// Consumer: a separate sandbox mounts the same volume by ID and reads the data
CreateSandboxFromSnapshotParams consumerParams = new CreateSandboxFromSnapshotParams();
consumerParams.setLanguage("python");
consumerParams.setVolumes(Collections.singletonList(mount));
Sandbox consumer = daytona.create(consumerParams);
byte[] data = consumer.fs.downloadFile(mountDir + "/payload.bin");
System.out.println(new String(data, StandardCharsets.UTF_8));
}
}
}
```
## Mount multiple volumes to one sandbox
Mount more than one volume to a single sandbox by passing entries in the `volumes` list. Use this pattern to combine shared assets, models, or datasets in one volume with separate per-application or per-user state in another, exposed at distinct mount paths.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount
daytona = Daytona()
shared_assets = daytona.volume.get("shared-assets", create=True)
logs = daytona.volume.get("logs", create=True)
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
language="python",
volumes=[
VolumeMount(volume_id=shared_assets.id, mount_path="/home/daytona/assets"),
VolumeMount(volume_id=logs.id, mount_path="/home/daytona/logs"),
],
))
```
```typescript
const sharedAssets = await daytona.volume.get('shared-assets', true)
const logs = await daytona.volume.get('logs', true)
const sandbox = await daytona.create({
language: 'typescript',
volumes: [
{ volumeId: sharedAssets.id, mountPath: '/home/daytona/assets' },
{ volumeId: logs.id, mountPath: '/home/daytona/logs' },
],
})
```
```ruby
shared_assets = daytona.volume.get('shared-assets', create: true)
logs = daytona.volume.get('logs', create: true)
params = Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::PYTHON,
volumes: [
DaytonaApiClient::SandboxVolume.new(volume_id: shared_assets.id, mount_path: '/home/daytona/assets'),
DaytonaApiClient::SandboxVolume.new(volume_id: logs.id, mount_path: '/home/daytona/logs')
]
)
sandbox = daytona.create(params)
```
```go
sharedAssets, err := client.Volume.Get(ctx, "shared-assets")
if err != nil {
sharedAssets, err = client.Volume.Create(ctx, "shared-assets")
if err != nil {
log.Fatal(err)
}
}
logs, err := client.Volume.Get(ctx, "logs")
if err != nil {
logs, err = client.Volume.Create(ctx, "logs")
if err != nil {
log.Fatal(err)
}
}
sandbox, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
Language: types.CodeLanguagePython,
Volumes: []types.VolumeMount{
{VolumeID: sharedAssets.ID, MountPath: "/home/daytona/assets"},
{VolumeID: logs.ID, MountPath: "/home/daytona/logs"},
},
},
})
if err != nil {
log.Fatal(err)
}
```
```java
Volume sharedAssets;
try {
sharedAssets = daytona.volume().getByName("shared-assets");
} catch (DaytonaNotFoundException e) {
sharedAssets = daytona.volume().create("shared-assets");
}
Volume logs;
try {
logs = daytona.volume().getByName("logs");
} catch (DaytonaNotFoundException e) {
logs = daytona.volume().create("logs");
}
VolumeMount assetsMount = new VolumeMount();
assetsMount.setVolumeId(sharedAssets.getId());
assetsMount.setMountPath("/home/daytona/assets");
VolumeMount logsMount = new VolumeMount();
logsMount.setVolumeId(logs.getId());
logsMount.setMountPath("/home/daytona/logs");
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setLanguage("python");
params.setVolumes(java.util.Arrays.asList(assetsMount, logsMount));
Sandbox sandbox = daytona.create(params);
```
## Multi-tenant isolation with subpaths
Isolate per-tenant or per-user data inside a single shared volume by setting a unique `subpath` on each sandbox's volume mount. Each sandbox sees only files under its assigned subpath at `mount_path` and cannot read or write sibling subpaths within the same volume. This is the recommended pattern for multi-tenant workloads because it stays within the [per-organization volume limit](#pricing--limits) instead of creating one volume per tenant.
Isolation is enforced at the FUSE mount boundary. Each sandbox sees its assigned subpath as the volume root, so a sandbox mounted at `users/alice` cannot reach `users/bob` through relative paths such as `../bob`.
```python
from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount
daytona = Daytona()
volume = daytona.volume.get("tenants", create=True)
mount_dir = "/home/daytona/data"
# Tenant A
alice_sandbox = daytona.create(CreateSandboxFromSnapshotParams(
language="python",
volumes=[VolumeMount(volume_id=volume.id, mount_path=mount_dir, subpath="users/alice")],
))
alice_sandbox.fs.upload_file(b"alice's data", f"{mount_dir}/notes.txt")
# Tenant B sees only its own subpath; alice's notes.txt is invisible
bob_sandbox = daytona.create(CreateSandboxFromSnapshotParams(
language="python",
volumes=[VolumeMount(volume_id=volume.id, mount_path=mount_dir, subpath="users/bob")],
))
bob_sandbox.fs.upload_file(b"bob's data", f"{mount_dir}/notes.txt")
```
```typescript
const volume = await daytona.volume.get('tenants', true)
const mountDir = '/home/daytona/data'
// Tenant A
const aliceSandbox = await daytona.create({
language: 'typescript',
volumes: [{ volumeId: volume.id, mountPath: mountDir, subpath: 'users/alice' }],
})
await aliceSandbox.fs.uploadFile(Buffer.from("alice's data"), `${mountDir}/notes.txt`)
// Tenant B sees only its own subpath; alice's notes.txt is invisible
const bobSandbox = await daytona.create({
language: 'typescript',
volumes: [{ volumeId: volume.id, mountPath: mountDir, subpath: 'users/bob' }],
})
await bobSandbox.fs.uploadFile(Buffer.from("bob's data"), `${mountDir}/notes.txt`)
```
```ruby
volume = daytona.volume.get('tenants', create: true)
mount_dir = '/home/daytona/data'
# Tenant A
alice_params = Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::PYTHON,
volumes: [DaytonaApiClient::SandboxVolume.new(
volume_id: volume.id, mount_path: mount_dir, subpath: 'users/alice'
)]
)
alice_sandbox = daytona.create(alice_params)
alice_sandbox.fs.upload_file("alice's data", "#{mount_dir}/notes.txt")
# Tenant B sees only its own subpath; alice's notes.txt is invisible
bob_params = Daytona::CreateSandboxFromSnapshotParams.new(
language: Daytona::CodeLanguage::PYTHON,
volumes: [DaytonaApiClient::SandboxVolume.new(
volume_id: volume.id, mount_path: mount_dir, subpath: 'users/bob'
)]
)
bob_sandbox = daytona.create(bob_params)
bob_sandbox.fs.upload_file("bob's data", "#{mount_dir}/notes.txt")
```
```go
volume, err := client.Volume.Get(ctx, "tenants")
if err != nil {
volume, err = client.Volume.Create(ctx, "tenants")
if err != nil {
log.Fatal(err)
}
}
mountDir := "/home/daytona/data"
// Tenant A
aliceSubpath := "users/alice"
aliceSandbox, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
Language: types.CodeLanguagePython,
Volumes: []types.VolumeMount{
{VolumeID: volume.ID, MountPath: mountDir, Subpath: &aliceSubpath},
},
},
})
if err != nil {
log.Fatal(err)
}
if err := aliceSandbox.FileSystem.UploadFile(ctx, []byte("alice's data"), mountDir+"/notes.txt"); err != nil {
log.Fatal(err)
}
// Tenant B sees only its own subpath; alice's notes.txt is invisible
bobSubpath := "users/bob"
bobSandbox, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
Language: types.CodeLanguagePython,
Volumes: []types.VolumeMount{
{VolumeID: volume.ID, MountPath: mountDir, Subpath: &bobSubpath},
},
},
})
if err != nil {
log.Fatal(err)
}
if err := bobSandbox.FileSystem.UploadFile(ctx, []byte("bob's data"), mountDir+"/notes.txt"); err != nil {
log.Fatal(err)
}
```
The Java SDK and CLI do not currently expose `subpath`. Use the REST API directly when you need subpath mounts from those clients.
```bash
# Tenant A
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"volumes": [
{
"volumeId": "",
"mountPath": "/home/daytona/data",
"subpath": "users/alice"
}
]
}'
# Tenant B
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Authorization: Bearer ' \
--header 'Content-Type: application/json' \
--data '{
"volumes": [
{
"volumeId": "",
"mountPath": "/home/daytona/data",
"subpath": "users/bob"
}
]
}'
```
## Limitations
Since volumes are FUSE-based mounts, they can not be used for applications that require block storage access (like database tables). Volumes are generally slower for both read and write operations compared to the local sandbox file system.
## Pricing & Limits
Daytona volumes are included at no additional cost. Each organization can create up to 100 volumes, and volume data does not count against your storage quota.
You can view your current volume usage in the [Daytona Volumes ↗](https://app.daytona.io/dashboard/volumes).
# Mount External Storage
Mount object storage (Amazon S3, Cloudflare R2, Tigris, Supabase Storage, Google Cloud Storage, Azure Blob), cloud storage like Box, and filesystems like Archil, MesaFS, and TiDB Cloud Filesystem into a Daytona sandbox as a regular directory. The sandbox reads from and writes to the bucket as if it were a local directory, so existing tools, scripts, and agents work without changes. This is useful for bringing in datasets, model weights, or build artifacts that already live in your own cloud account.
External storage mounts and [Daytona Volumes](https://www.daytona.io/docs/en/volumes.md) are complementary FUSE-based mechanisms — both expose remote object storage as a regular sandbox directory, both can be shared across sandboxes, and both persist beyond any individual sandbox's lifetime. The main distinction is **where the data physically lives**: Daytona Volumes are hosted on Daytona's own S3-compatible object store, while external mounts connect to a bucket or filesystem hosted on another provider (Amazon S3, Cloudflare R2, Tigris, Supabase Storage, GCS, Azure Blob, Box, Archil, MesaFS, TiDB Cloud Filesystem).
External storage is mounted using FUSE. Daytona supports two approaches, and each provider section below shows both — pick whichever fits your workflow:
- **Pre-built snapshot** — build a [snapshot](https://www.daytona.io/docs/en/snapshots.md) once with the FUSE tool (`mount-s3`, `gcsfuse`, `blobfuse2`, `box-mount`) built-in, then launch every sandbox from that snapshot. Cold starts are fast and predictable. Best for production.
- **Runtime install** — launch a default sandbox and `apt-get install` the FUSE tool when the sandbox starts. Adds time to sandbox startup, but you don't manage snapshots. Best for quick experiments.
Both approaches end with the same mount command and the same usage — the only difference is when the FUSE tool gets installed.
## Mount an Amazon S3 bucket
Mount an S3 bucket using [Mountpoint for Amazon S3 ↗](https://github.com/awslabs/mountpoint-s3) — AWS's official FUSE client, optimized for high throughput on S3.
**Credentials** — set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in your local environment. The snippets below pass them into the sandbox via `envVars`, and `mount-s3` reads them from there.
### Pre-built snapshot
Build a snapshot with `mount-s3` preinstalled, then launch all S3-enabled sandboxes from that snapshot. This removes per-sandbox package install work, keeps cold starts predictable, and gives you a reusable baseline image for production workloads.
#### Build a snapshot
Create a reusable snapshot that installs `mount-s3` and its system dependencies. After it finishes, every sandbox launched from `fuse-s3` already has the mount binary available.
```python
from daytona import CreateSnapshotParams, Daytona, Image
daytona = Daytona()
image = (
Image.base("daytonaio/sandbox")
.run_commands(
"sudo apt-get update "
"&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget",
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" '
'&& wget -O /tmp/mount-s3.deb '
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" '
"&& sudo apt-get install -y /tmp/mount-s3.deb "
"&& rm /tmp/mount-s3.deb",
)
)
daytona.snapshot.create(
CreateSnapshotParams(name="fuse-s3", image=image),
on_logs=print,
)
```
```typescript
import { Daytona, Image } from '@daytona/sdk'
const daytona = new Daytona()
const image = Image.base('daytonaio/sandbox').runCommands(
'sudo apt-get update ' +
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget',
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' +
'&& wget -O /tmp/mount-s3.deb ' +
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' +
'&& sudo apt-get install -y /tmp/mount-s3.deb ' +
'&& rm /tmp/mount-s3.deb',
)
await daytona.snapshot.create(
{ name: 'fuse-s3', image },
{ onLogs: console.log },
)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
image = Daytona::Image
.base('daytonaio/sandbox')
.run_commands(
'sudo apt-get update ' \
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget',
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' \
'&& wget -O /tmp/mount-s3.deb ' \
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' \
'&& sudo apt-get install -y /tmp/mount-s3.deb ' \
'&& rm /tmp/mount-s3.deb'
)
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(name: 'fuse-s3', image: image),
on_logs: proc { |chunk| print(chunk) }
)
```
```go
import (
"context"
"fmt"
"log"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
image := daytona.Base("daytonaio/sandbox").
Run("sudo apt-get update && sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget").
Run(`arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" && ` +
`wget -O /tmp/mount-s3.deb "https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" && ` +
`sudo apt-get install -y /tmp/mount-s3.deb && rm /tmp/mount-s3.deb`)
_, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "fuse-s3",
Image: image,
})
if err != nil {
log.Fatal(err)
}
for line := range logChan {
fmt.Print(line)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Image;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Image image = Image.base("daytonaio/sandbox")
.runCommands(
"sudo apt-get update "
+ "&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget",
"arch=\"$(dpkg --print-architecture | sed s/amd64/x86_64/)\" "
+ "&& wget -O /tmp/mount-s3.deb "
+ "\"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb\" "
+ "&& sudo apt-get install -y /tmp/mount-s3.deb "
+ "&& rm /tmp/mount-s3.deb"
);
daytona.snapshot().create("fuse-s3", image, System.out::println);
}
}
}
```
#### Launch and mount
Pass AWS credentials as environment variables on sandbox creation. `mount-s3` reads them automatically.
```python
import os
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="fuse-s3",
env_vars={
"AWS_ACCESS_KEY_ID": os.environ["AWS_ACCESS_KEY_ID"],
"AWS_SECRET_ACCESS_KEY": os.environ["AWS_SECRET_ACCESS_KEY"],
},
)
)
mount_path = "/home/daytona/s3"
# mount-s3 daemonizes by default and reads AWS_* from the environment
sandbox.process.exec(f"mkdir -p {mount_path}")
sandbox.process.exec(f"mount-s3 my-bucket {mount_path}")
# Read and write through the mount as if it were a local directory
sandbox.process.exec(f"echo 'hello from Daytona' > {mount_path}/hello.txt")
response = sandbox.process.exec(f"cat {mount_path}/hello.txt")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: 'fuse-s3',
envVars: {
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID!,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY!,
},
})
const mountPath = '/home/daytona/s3'
// mount-s3 daemonizes by default and reads AWS_* from the environment
await sandbox.process.executeCommand(`mkdir -p ${mountPath}`)
await sandbox.process.executeCommand(`mount-s3 my-bucket ${mountPath}`)
// Read and write through the mount as if it were a local directory
await sandbox.process.executeCommand(`echo 'hello from Daytona' > ${mountPath}/hello.txt`)
const response = await sandbox.process.executeCommand(`cat ${mountPath}/hello.txt`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'fuse-s3',
env_vars: {
'AWS_ACCESS_KEY_ID' => ENV.fetch('AWS_ACCESS_KEY_ID'),
'AWS_SECRET_ACCESS_KEY' => ENV.fetch('AWS_SECRET_ACCESS_KEY')
}
)
)
mount_path = '/home/daytona/s3'
# mount-s3 daemonizes by default and reads AWS_* from the environment
sandbox.process.exec(command: "mkdir -p #{mount_path}")
sandbox.process.exec(command: "mount-s3 my-bucket #{mount_path}")
# Read and write through the mount as if it were a local directory
sandbox.process.exec(command: "echo 'hello from Daytona' > #{mount_path}/hello.txt")
response = sandbox.process.exec(command: "cat #{mount_path}/hello.txt")
puts response.result
```
```go
import (
"context"
"fmt"
"log"
"os"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: "fuse-s3",
SandboxBaseParams: types.SandboxBaseParams{
EnvVars: map[string]string{
"AWS_ACCESS_KEY_ID": os.Getenv("AWS_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY": os.Getenv("AWS_SECRET_ACCESS_KEY"),
},
},
})
if err != nil {
log.Fatal(err)
}
mountPath := "/home/daytona/s3"
// mount-s3 daemonizes by default and reads AWS_* from the environment
if _, err := sandbox.Process.ExecuteCommand(ctx, "mkdir -p "+mountPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "mount-s3 my-bucket "+mountPath); err != nil {
log.Fatal(err)
}
// Read and write through the mount as if it were a local directory
if _, err := sandbox.Process.ExecuteCommand(ctx, "echo 'hello from Daytona' > "+mountPath+"/hello.txt"); err != nil {
log.Fatal(err)
}
response, err := sandbox.Process.ExecuteCommand(ctx, "cat "+mountPath+"/hello.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Result)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
import java.util.Map;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("fuse-s3");
params.setEnvVars(Map.of(
"AWS_ACCESS_KEY_ID", System.getenv("AWS_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY", System.getenv("AWS_SECRET_ACCESS_KEY")
));
Sandbox sandbox = daytona.create(params);
String mountPath = "/home/daytona/s3";
// mount-s3 daemonizes by default and reads AWS_* from the environment
sandbox.getProcess().executeCommand("mkdir -p " + mountPath);
sandbox.getProcess().executeCommand("mount-s3 my-bucket " + mountPath);
// Read and write through the mount as if it were a local directory
sandbox.getProcess().executeCommand(
"echo 'hello from Daytona' > " + mountPath + "/hello.txt");
ExecuteResponse response = sandbox.getProcess().executeCommand(
"cat " + mountPath + "/hello.txt");
System.out.println(response.getResult());
}
}
}
```
### Runtime install
Start from a default sandbox and install `mount-s3` during startup before running the mount command. This is useful for quick testing and temporary environments where you do not want to maintain a custom snapshot, with the tradeoff of slower cold starts.
```python
import os
from daytona import CreateSandboxBaseParams, Daytona
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxBaseParams(
env_vars={
"AWS_ACCESS_KEY_ID": os.environ["AWS_ACCESS_KEY_ID"],
"AWS_SECRET_ACCESS_KEY": os.environ["AWS_SECRET_ACCESS_KEY"],
},
)
)
# Install mount-s3 at runtime
sandbox.process.exec(
"sudo apt-get update "
"&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget"
)
sandbox.process.exec(
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" '
'&& wget -O /tmp/mount-s3.deb '
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" '
"&& sudo apt-get install -y /tmp/mount-s3.deb"
)
# Mount and use
mount_path = "/home/daytona/s3"
sandbox.process.exec(f"mkdir -p {mount_path} && mount-s3 my-bucket {mount_path}")
response = sandbox.process.exec(f"ls {mount_path}")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
envVars: {
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID!,
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY!,
},
})
// Install mount-s3 at runtime
await sandbox.process.executeCommand(
'sudo apt-get update ' +
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget',
)
await sandbox.process.executeCommand(
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' +
'&& wget -O /tmp/mount-s3.deb ' +
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' +
'&& sudo apt-get install -y /tmp/mount-s3.deb',
)
// Mount and use
const mountPath = '/home/daytona/s3'
await sandbox.process.executeCommand(`mkdir -p ${mountPath} && mount-s3 my-bucket ${mountPath}`)
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxBaseParams.new(
env_vars: {
'AWS_ACCESS_KEY_ID' => ENV.fetch('AWS_ACCESS_KEY_ID'),
'AWS_SECRET_ACCESS_KEY' => ENV.fetch('AWS_SECRET_ACCESS_KEY')
}
)
)
# Install mount-s3 at runtime
sandbox.process.exec(
command: 'sudo apt-get update ' \
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget'
)
sandbox.process.exec(
command: 'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' \
'&& wget -O /tmp/mount-s3.deb ' \
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' \
'&& sudo apt-get install -y /tmp/mount-s3.deb'
)
# Mount and use
mount_path = '/home/daytona/s3'
sandbox.process.exec(command: "mkdir -p #{mount_path} && mount-s3 my-bucket #{mount_path}")
response = sandbox.process.exec(command: "ls #{mount_path}")
puts response.result
```
```go
import (
"context"
"fmt"
"log"
"os"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
sandbox, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
EnvVars: map[string]string{
"AWS_ACCESS_KEY_ID": os.Getenv("AWS_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY": os.Getenv("AWS_SECRET_ACCESS_KEY"),
},
},
})
if err != nil {
log.Fatal(err)
}
// Install mount-s3 at runtime
if _, err := sandbox.Process.ExecuteCommand(ctx,
"sudo apt-get update && sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
`arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" && `+
`wget -O /tmp/mount-s3.deb "https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" && `+
`sudo apt-get install -y /tmp/mount-s3.deb`); err != nil {
log.Fatal(err)
}
// Mount and use
mountPath := "/home/daytona/s3"
if _, err := sandbox.Process.ExecuteCommand(ctx,
"mkdir -p "+mountPath+" && mount-s3 my-bucket "+mountPath); err != nil {
log.Fatal(err)
}
response, err := sandbox.Process.ExecuteCommand(ctx, "ls "+mountPath)
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Result)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
import java.util.Map;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setEnvVars(Map.of(
"AWS_ACCESS_KEY_ID", System.getenv("AWS_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY", System.getenv("AWS_SECRET_ACCESS_KEY")
));
Sandbox sandbox = daytona.create(params);
// Install mount-s3 at runtime
sandbox.getProcess().executeCommand(
"sudo apt-get update "
+ "&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget");
sandbox.getProcess().executeCommand(
"arch=\"$(dpkg --print-architecture | sed s/amd64/x86_64/)\" "
+ "&& wget -O /tmp/mount-s3.deb "
+ "\"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb\" "
+ "&& sudo apt-get install -y /tmp/mount-s3.deb");
// Mount and use
String mountPath = "/home/daytona/s3";
sandbox.getProcess().executeCommand(
"mkdir -p " + mountPath + " && mount-s3 my-bucket " + mountPath);
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
## Mount a Cloudflare R2 bucket
Cloudflare R2 is S3-compatible, so the same `mount-s3` tool works. Pass an explicit `--endpoint-url` pointing at your R2 account.
**Credentials** — set `R2_ACCOUNT_ID`, `R2_ACCESS_KEY_ID`, and `R2_SECRET_ACCESS_KEY` in your local environment. R2 is S3-compatible, so the snippets below pass your R2 keys into the sandbox via `envVars` under the `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` names that `mount-s3` expects.
### Pre-built snapshot
Build a snapshot with `mount-s3` preinstalled, then launch all R2-enabled sandboxes from that snapshot. The mount flow stays identical to S3 except for the R2 `--endpoint-url`, and startup remains fast because installation is done once at snapshot build time.
#### Build a snapshot
Create a reusable snapshot that installs the same `mount-s3` tool used for S3. R2 remains S3-compatible, so this snapshot is identical to S3 setup and only the runtime mount command changes.
```python
from daytona import CreateSnapshotParams, Daytona, Image
daytona = Daytona()
image = (
Image.base("daytonaio/sandbox")
.run_commands(
"sudo apt-get update "
"&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget",
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" '
'&& wget -O /tmp/mount-s3.deb '
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" '
"&& sudo apt-get install -y /tmp/mount-s3.deb "
"&& rm /tmp/mount-s3.deb",
)
)
daytona.snapshot.create(
CreateSnapshotParams(name="fuse-r2", image=image),
on_logs=print,
)
```
```typescript
import { Daytona, Image } from '@daytona/sdk'
const daytona = new Daytona()
const image = Image.base('daytonaio/sandbox').runCommands(
'sudo apt-get update ' +
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget',
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' +
'&& wget -O /tmp/mount-s3.deb ' +
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' +
'&& sudo apt-get install -y /tmp/mount-s3.deb ' +
'&& rm /tmp/mount-s3.deb',
)
await daytona.snapshot.create(
{ name: 'fuse-r2', image },
{ onLogs: console.log },
)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
image = Daytona::Image
.base('daytonaio/sandbox')
.run_commands(
'sudo apt-get update ' \
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget',
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' \
'&& wget -O /tmp/mount-s3.deb ' \
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' \
'&& sudo apt-get install -y /tmp/mount-s3.deb ' \
'&& rm /tmp/mount-s3.deb'
)
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(name: 'fuse-r2', image: image),
on_logs: proc { |chunk| print(chunk) }
)
```
```go
import (
"context"
"fmt"
"log"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
image := daytona.Base("daytonaio/sandbox").
Run("sudo apt-get update && sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget").
Run(`arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" && ` +
`wget -O /tmp/mount-s3.deb "https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" && ` +
`sudo apt-get install -y /tmp/mount-s3.deb && rm /tmp/mount-s3.deb`)
_, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "fuse-r2",
Image: image,
})
if err != nil {
log.Fatal(err)
}
for line := range logChan {
fmt.Print(line)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Image;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Image image = Image.base("daytonaio/sandbox")
.runCommands(
"sudo apt-get update "
+ "&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget",
"arch=\"$(dpkg --print-architecture | sed s/amd64/x86_64/)\" "
+ "&& wget -O /tmp/mount-s3.deb "
+ "\"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb\" "
+ "&& sudo apt-get install -y /tmp/mount-s3.deb "
+ "&& rm /tmp/mount-s3.deb"
);
daytona.snapshot().create("fuse-r2", image, System.out::println);
}
}
}
```
#### Launch and mount
Pass your R2 credentials into the sandbox as `AWS_*` environment variables and mount with the R2 endpoint URL. This keeps the authentication flow compatible with `mount-s3` while targeting your Cloudflare account.
```python
import os
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
# R2 credentials live in your Cloudflare dashboard under R2 > Manage API Tokens
account_id = os.environ["R2_ACCOUNT_ID"]
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="fuse-r2",
env_vars={
"AWS_ACCESS_KEY_ID": os.environ["R2_ACCESS_KEY_ID"],
"AWS_SECRET_ACCESS_KEY": os.environ["R2_SECRET_ACCESS_KEY"],
},
)
)
mount_path = "/home/daytona/r2"
sandbox.process.exec(f"mkdir -p {mount_path}")
sandbox.process.exec(
f"mount-s3 --endpoint-url https://{account_id}.r2.cloudflarestorage.com "
f"my-r2-bucket {mount_path}"
)
response = sandbox.process.exec(f"ls {mount_path}")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
// R2 credentials live in your Cloudflare dashboard under R2 > Manage API Tokens
const accountId = process.env.R2_ACCOUNT_ID!
const sandbox = await daytona.create({
snapshot: 'fuse-r2',
envVars: {
AWS_ACCESS_KEY_ID: process.env.R2_ACCESS_KEY_ID!,
AWS_SECRET_ACCESS_KEY: process.env.R2_SECRET_ACCESS_KEY!,
},
})
const mountPath = '/home/daytona/r2'
await sandbox.process.executeCommand(`mkdir -p ${mountPath}`)
await sandbox.process.executeCommand(
`mount-s3 --endpoint-url https://${accountId}.r2.cloudflarestorage.com ` +
`my-r2-bucket ${mountPath}`,
)
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
# R2 credentials live in your Cloudflare dashboard under R2 > Manage API Tokens
account_id = ENV.fetch('R2_ACCOUNT_ID')
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'fuse-r2',
env_vars: {
'AWS_ACCESS_KEY_ID' => ENV.fetch('R2_ACCESS_KEY_ID'),
'AWS_SECRET_ACCESS_KEY' => ENV.fetch('R2_SECRET_ACCESS_KEY')
}
)
)
mount_path = '/home/daytona/r2'
sandbox.process.exec(command: "mkdir -p #{mount_path}")
sandbox.process.exec(
command: "mount-s3 --endpoint-url https://#{account_id}.r2.cloudflarestorage.com " \
"my-r2-bucket #{mount_path}"
)
response = sandbox.process.exec(command: "ls #{mount_path}")
puts response.result
```
```go
import (
"context"
"fmt"
"log"
"os"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
// R2 credentials live in your Cloudflare dashboard under R2 > Manage API Tokens
accountID := os.Getenv("R2_ACCOUNT_ID")
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: "fuse-r2",
SandboxBaseParams: types.SandboxBaseParams{
EnvVars: map[string]string{
"AWS_ACCESS_KEY_ID": os.Getenv("R2_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY": os.Getenv("R2_SECRET_ACCESS_KEY"),
},
},
})
if err != nil {
log.Fatal(err)
}
mountPath := "/home/daytona/r2"
if _, err := sandbox.Process.ExecuteCommand(ctx, "mkdir -p "+mountPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
"mount-s3 --endpoint-url https://"+accountID+".r2.cloudflarestorage.com "+
"my-r2-bucket "+mountPath); err != nil {
log.Fatal(err)
}
response, err := sandbox.Process.ExecuteCommand(ctx, "ls "+mountPath)
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Result)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
import java.util.Map;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
// R2 credentials live in your Cloudflare dashboard under R2 > Manage API Tokens
String accountId = System.getenv("R2_ACCOUNT_ID");
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("fuse-r2");
params.setEnvVars(Map.of(
"AWS_ACCESS_KEY_ID", System.getenv("R2_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY", System.getenv("R2_SECRET_ACCESS_KEY")
));
Sandbox sandbox = daytona.create(params);
String mountPath = "/home/daytona/r2";
sandbox.getProcess().executeCommand("mkdir -p " + mountPath);
sandbox.getProcess().executeCommand(
"mount-s3 --endpoint-url https://" + accountId + ".r2.cloudflarestorage.com "
+ "my-r2-bucket " + mountPath);
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
### Runtime install
Start from a default sandbox and install `mount-s3` during startup, then mount your bucket with the R2 `--endpoint-url`. This path is convenient for prototyping or one-off tasks, but each new sandbox pays the package installation cost.
```python
import os
from daytona import CreateSandboxBaseParams, Daytona
daytona = Daytona()
account_id = os.environ["R2_ACCOUNT_ID"]
sandbox = daytona.create(
CreateSandboxBaseParams(
env_vars={
"AWS_ACCESS_KEY_ID": os.environ["R2_ACCESS_KEY_ID"],
"AWS_SECRET_ACCESS_KEY": os.environ["R2_SECRET_ACCESS_KEY"],
},
)
)
# Install mount-s3
sandbox.process.exec(
"sudo apt-get update "
"&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget"
)
sandbox.process.exec(
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" '
'&& wget -O /tmp/mount-s3.deb '
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" '
"&& sudo apt-get install -y /tmp/mount-s3.deb"
)
# Mount with R2 endpoint
mount_path = "/home/daytona/r2"
sandbox.process.exec(
f"mkdir -p {mount_path} && "
f"mount-s3 --endpoint-url https://{account_id}.r2.cloudflarestorage.com "
f"my-r2-bucket {mount_path}"
)
response = sandbox.process.exec(f"ls {mount_path}")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const accountId = process.env.R2_ACCOUNT_ID!
const sandbox = await daytona.create({
envVars: {
AWS_ACCESS_KEY_ID: process.env.R2_ACCESS_KEY_ID!,
AWS_SECRET_ACCESS_KEY: process.env.R2_SECRET_ACCESS_KEY!,
},
})
// Install mount-s3
await sandbox.process.executeCommand(
'sudo apt-get update ' +
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget',
)
await sandbox.process.executeCommand(
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' +
'&& wget -O /tmp/mount-s3.deb ' +
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' +
'&& sudo apt-get install -y /tmp/mount-s3.deb',
)
// Mount with R2 endpoint
const mountPath = '/home/daytona/r2'
await sandbox.process.executeCommand(
`mkdir -p ${mountPath} && ` +
`mount-s3 --endpoint-url https://${accountId}.r2.cloudflarestorage.com ` +
`my-r2-bucket ${mountPath}`,
)
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
account_id = ENV.fetch('R2_ACCOUNT_ID')
sandbox = daytona.create(
Daytona::CreateSandboxBaseParams.new(
env_vars: {
'AWS_ACCESS_KEY_ID' => ENV.fetch('R2_ACCESS_KEY_ID'),
'AWS_SECRET_ACCESS_KEY' => ENV.fetch('R2_SECRET_ACCESS_KEY')
}
)
)
# Install mount-s3
sandbox.process.exec(
command: 'sudo apt-get update ' \
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget'
)
sandbox.process.exec(
command: 'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' \
'&& wget -O /tmp/mount-s3.deb ' \
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' \
'&& sudo apt-get install -y /tmp/mount-s3.deb'
)
# Mount with R2 endpoint
mount_path = '/home/daytona/r2'
sandbox.process.exec(
command: "mkdir -p #{mount_path} && " \
"mount-s3 --endpoint-url https://#{account_id}.r2.cloudflarestorage.com " \
"my-r2-bucket #{mount_path}"
)
response = sandbox.process.exec(command: "ls #{mount_path}")
puts response.result
```
```go
import (
"context"
"fmt"
"log"
"os"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
accountID := os.Getenv("R2_ACCOUNT_ID")
sandbox, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
EnvVars: map[string]string{
"AWS_ACCESS_KEY_ID": os.Getenv("R2_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY": os.Getenv("R2_SECRET_ACCESS_KEY"),
},
},
})
if err != nil {
log.Fatal(err)
}
// Install mount-s3
if _, err := sandbox.Process.ExecuteCommand(ctx,
"sudo apt-get update && sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
`arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" && `+
`wget -O /tmp/mount-s3.deb "https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" && `+
`sudo apt-get install -y /tmp/mount-s3.deb`); err != nil {
log.Fatal(err)
}
// Mount with R2 endpoint
mountPath := "/home/daytona/r2"
if _, err := sandbox.Process.ExecuteCommand(ctx,
"mkdir -p "+mountPath+" && "+
"mount-s3 --endpoint-url https://"+accountID+".r2.cloudflarestorage.com "+
"my-r2-bucket "+mountPath); err != nil {
log.Fatal(err)
}
response, err := sandbox.Process.ExecuteCommand(ctx, "ls "+mountPath)
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Result)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
import java.util.Map;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
String accountId = System.getenv("R2_ACCOUNT_ID");
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setEnvVars(Map.of(
"AWS_ACCESS_KEY_ID", System.getenv("R2_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY", System.getenv("R2_SECRET_ACCESS_KEY")
));
Sandbox sandbox = daytona.create(params);
// Install mount-s3
sandbox.getProcess().executeCommand(
"sudo apt-get update "
+ "&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget");
sandbox.getProcess().executeCommand(
"arch=\"$(dpkg --print-architecture | sed s/amd64/x86_64/)\" "
+ "&& wget -O /tmp/mount-s3.deb "
+ "\"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb\" "
+ "&& sudo apt-get install -y /tmp/mount-s3.deb");
// Mount with R2 endpoint
String mountPath = "/home/daytona/r2";
sandbox.getProcess().executeCommand(
"mkdir -p " + mountPath + " && "
+ "mount-s3 --endpoint-url https://" + accountId + ".r2.cloudflarestorage.com "
+ "my-r2-bucket " + mountPath);
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
## Mount a Tigris bucket
Mount a Tigris bucket with the same `mount-s3` tool used for S3. Pass `--endpoint-url https://t3.storage.dev`, because Tigris uses one global endpoint with no per-account subdomain. Tigris also supports bucket snapshots and copy-on-write forks through request headers, so each sandbox can use an isolated writable bucket without duplicating source data.
**Credentials** — set `TIGRIS_STORAGE_ACCESS_KEY_ID` and `TIGRIS_STORAGE_SECRET_ACCESS_KEY` in your local environment. The snippets below pass these into the sandbox via `envVars` under the `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` names that `mount-s3` expects.
### Pre-built snapshot
Build a snapshot with `mount-s3` preinstalled, then launch Tigris sandboxes from that snapshot. The mount flow matches S3 except for the Tigris `--endpoint-url`, and startup stays fast because installation happens once during snapshot build.
#### Build a snapshot
Create a reusable snapshot that installs `mount-s3`. Because Tigris is S3-compatible, this setup matches S3 and only the runtime mount command changes.
```python
from daytona import CreateSnapshotParams, Daytona, Image
daytona = Daytona()
image = (
Image.base("daytonaio/sandbox")
.run_commands(
"sudo apt-get update "
"&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget",
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" '
'&& wget -O /tmp/mount-s3.deb '
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" '
"&& sudo apt-get install -y /tmp/mount-s3.deb "
"&& rm /tmp/mount-s3.deb",
)
)
daytona.snapshot.create(
CreateSnapshotParams(name="fuse-tigris", image=image),
on_logs=print,
)
```
```typescript
import { Daytona, Image } from '@daytona/sdk'
const daytona = new Daytona()
const image = Image.base('daytonaio/sandbox').runCommands(
'sudo apt-get update ' +
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget',
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' +
'&& wget -O /tmp/mount-s3.deb ' +
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' +
'&& sudo apt-get install -y /tmp/mount-s3.deb ' +
'&& rm /tmp/mount-s3.deb',
)
await daytona.snapshot.create(
{ name: 'fuse-tigris', image },
{ onLogs: console.log },
)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
image = Daytona::Image
.base('daytonaio/sandbox')
.run_commands(
'sudo apt-get update ' \
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget',
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' \
'&& wget -O /tmp/mount-s3.deb ' \
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' \
'&& sudo apt-get install -y /tmp/mount-s3.deb ' \
'&& rm /tmp/mount-s3.deb'
)
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(name: 'fuse-tigris', image: image),
on_logs: proc { |chunk| print(chunk) }
)
```
```go
import (
"context"
"fmt"
"log"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
image := daytona.Base("daytonaio/sandbox").
Run("sudo apt-get update && sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget").
Run(`arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" && ` +
`wget -O /tmp/mount-s3.deb "https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" && ` +
`sudo apt-get install -y /tmp/mount-s3.deb && rm /tmp/mount-s3.deb`)
_, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "fuse-tigris",
Image: image,
})
if err != nil {
log.Fatal(err)
}
for line := range logChan {
fmt.Print(line)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Image;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Image image = Image.base("daytonaio/sandbox")
.runCommands(
"sudo apt-get update "
+ "&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget",
"arch=\"$(dpkg --print-architecture | sed s/amd64/x86_64/)\" "
+ "&& wget -O /tmp/mount-s3.deb "
+ "\"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb\" "
+ "&& sudo apt-get install -y /tmp/mount-s3.deb "
+ "&& rm /tmp/mount-s3.deb"
);
daytona.snapshot().create("fuse-tigris", image, System.out::println);
}
}
}
```
#### Launch and mount
Pass Tigris credentials into the sandbox as `AWS_*` environment variables, then mount with the Tigris endpoint URL. This keeps authentication compatible with `mount-s3` while targeting your Tigris account.
```python
import os
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="fuse-tigris",
env_vars={
"AWS_ACCESS_KEY_ID": os.environ["TIGRIS_STORAGE_ACCESS_KEY_ID"],
"AWS_SECRET_ACCESS_KEY": os.environ["TIGRIS_STORAGE_SECRET_ACCESS_KEY"],
},
)
)
mount_path = "/home/daytona/tigris"
sandbox.process.exec(f"mkdir -p {mount_path}")
sandbox.process.exec(
f"mount-s3 --endpoint-url https://t3.storage.dev "
f"my-tigris-bucket {mount_path}"
)
response = sandbox.process.exec(f"ls {mount_path}")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: 'fuse-tigris',
envVars: {
AWS_ACCESS_KEY_ID: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID!,
AWS_SECRET_ACCESS_KEY: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY!,
},
})
const mountPath = '/home/daytona/tigris'
await sandbox.process.executeCommand(`mkdir -p ${mountPath}`)
await sandbox.process.executeCommand(
`mount-s3 --endpoint-url https://t3.storage.dev ` +
`my-tigris-bucket ${mountPath}`,
)
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'fuse-tigris',
env_vars: {
'AWS_ACCESS_KEY_ID' => ENV.fetch('TIGRIS_STORAGE_ACCESS_KEY_ID'),
'AWS_SECRET_ACCESS_KEY' => ENV.fetch('TIGRIS_STORAGE_SECRET_ACCESS_KEY')
}
)
)
mount_path = '/home/daytona/tigris'
sandbox.process.exec(command: "mkdir -p #{mount_path}")
sandbox.process.exec(
command: 'mount-s3 --endpoint-url https://t3.storage.dev ' \
"my-tigris-bucket #{mount_path}"
)
response = sandbox.process.exec(command: "ls #{mount_path}")
puts response.result
```
```go
import (
"context"
"fmt"
"log"
"os"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: "fuse-tigris",
SandboxBaseParams: types.SandboxBaseParams{
EnvVars: map[string]string{
"AWS_ACCESS_KEY_ID": os.Getenv("TIGRIS_STORAGE_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY": os.Getenv("TIGRIS_STORAGE_SECRET_ACCESS_KEY"),
},
},
})
if err != nil {
log.Fatal(err)
}
mountPath := "/home/daytona/tigris"
if _, err := sandbox.Process.ExecuteCommand(ctx, "mkdir -p "+mountPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
"mount-s3 --endpoint-url https://t3.storage.dev "+
"my-tigris-bucket "+mountPath); err != nil {
log.Fatal(err)
}
response, err := sandbox.Process.ExecuteCommand(ctx, "ls "+mountPath)
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Result)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
import java.util.Map;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("fuse-tigris");
params.setEnvVars(Map.of(
"AWS_ACCESS_KEY_ID", System.getenv("TIGRIS_STORAGE_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY", System.getenv("TIGRIS_STORAGE_SECRET_ACCESS_KEY")
));
Sandbox sandbox = daytona.create(params);
String mountPath = "/home/daytona/tigris";
sandbox.getProcess().executeCommand("mkdir -p " + mountPath);
sandbox.getProcess().executeCommand(
"mount-s3 --endpoint-url https://t3.storage.dev "
+ "my-tigris-bucket " + mountPath);
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
### Runtime install
Start from a default sandbox, install `mount-s3` during startup, then mount with the Tigris `--endpoint-url`. This path is convenient for prototyping or one-off tasks, but each new sandbox repeats package installation.
```python
import os
from daytona import CreateSandboxBaseParams, Daytona
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxBaseParams(
env_vars={
"AWS_ACCESS_KEY_ID": os.environ["TIGRIS_STORAGE_ACCESS_KEY_ID"],
"AWS_SECRET_ACCESS_KEY": os.environ["TIGRIS_STORAGE_SECRET_ACCESS_KEY"],
},
)
)
# Install mount-s3
sandbox.process.exec(
"sudo apt-get update "
"&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget"
)
sandbox.process.exec(
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" '
'&& wget -O /tmp/mount-s3.deb '
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" '
"&& sudo apt-get install -y /tmp/mount-s3.deb"
)
# Mount with Tigris endpoint
mount_path = "/home/daytona/tigris"
sandbox.process.exec(
f"mkdir -p {mount_path} && "
f"mount-s3 --endpoint-url https://t3.storage.dev "
f"my-tigris-bucket {mount_path}"
)
response = sandbox.process.exec(f"ls {mount_path}")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const sandbox = await daytona.create({
envVars: {
AWS_ACCESS_KEY_ID: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID!,
AWS_SECRET_ACCESS_KEY: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY!,
},
})
// Install mount-s3
await sandbox.process.executeCommand(
'sudo apt-get update ' +
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget',
)
await sandbox.process.executeCommand(
'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' +
'&& wget -O /tmp/mount-s3.deb ' +
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' +
'&& sudo apt-get install -y /tmp/mount-s3.deb',
)
// Mount with Tigris endpoint
const mountPath = '/home/daytona/tigris'
await sandbox.process.executeCommand(
`mkdir -p ${mountPath} && ` +
`mount-s3 --endpoint-url https://t3.storage.dev ` +
`my-tigris-bucket ${mountPath}`,
)
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxBaseParams.new(
env_vars: {
'AWS_ACCESS_KEY_ID' => ENV.fetch('TIGRIS_STORAGE_ACCESS_KEY_ID'),
'AWS_SECRET_ACCESS_KEY' => ENV.fetch('TIGRIS_STORAGE_SECRET_ACCESS_KEY')
}
)
)
# Install mount-s3
sandbox.process.exec(
command: 'sudo apt-get update ' \
'&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget'
)
sandbox.process.exec(
command: 'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' \
'&& wget -O /tmp/mount-s3.deb ' \
'"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' \
'&& sudo apt-get install -y /tmp/mount-s3.deb'
)
# Mount with Tigris endpoint
mount_path = '/home/daytona/tigris'
sandbox.process.exec(
command: "mkdir -p #{mount_path} && " \
'mount-s3 --endpoint-url https://t3.storage.dev ' \
"my-tigris-bucket #{mount_path}"
)
response = sandbox.process.exec(command: "ls #{mount_path}")
puts response.result
```
```go
import (
"context"
"fmt"
"log"
"os"
"github.com/daytona/clients/sdk-go/pkg/daytona"
"github.com/daytona/clients/sdk-go/pkg/types"
)
ctx := context.Background()
client, err := daytona.NewClient()
if err != nil {
log.Fatal(err)
}
sandbox, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
EnvVars: map[string]string{
"AWS_ACCESS_KEY_ID": os.Getenv("TIGRIS_STORAGE_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY": os.Getenv("TIGRIS_STORAGE_SECRET_ACCESS_KEY"),
},
},
})
if err != nil {
log.Fatal(err)
}
// Install mount-s3
if _, err := sandbox.Process.ExecuteCommand(ctx,
"sudo apt-get update && sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
`arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" && `+
`wget -O /tmp/mount-s3.deb "https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" && `+
`sudo apt-get install -y /tmp/mount-s3.deb`); err != nil {
log.Fatal(err)
}
// Mount with Tigris endpoint
mountPath := "/home/daytona/tigris"
if _, err := sandbox.Process.ExecuteCommand(ctx,
"mkdir -p "+mountPath+" && "+
"mount-s3 --endpoint-url https://t3.storage.dev "+
"my-tigris-bucket "+mountPath); err != nil {
log.Fatal(err)
}
response, err := sandbox.Process.ExecuteCommand(ctx, "ls "+mountPath)
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Result)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
import io.daytona.sdk.model.ExecuteResponse;
import java.util.Map;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setEnvVars(Map.of(
"AWS_ACCESS_KEY_ID", System.getenv("TIGRIS_STORAGE_ACCESS_KEY_ID"),
"AWS_SECRET_ACCESS_KEY", System.getenv("TIGRIS_STORAGE_SECRET_ACCESS_KEY")
));
Sandbox sandbox = daytona.create(params);
// Install mount-s3
sandbox.getProcess().executeCommand(
"sudo apt-get update "
+ "&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget");
sandbox.getProcess().executeCommand(
"arch=\"$(dpkg --print-architecture | sed s/amd64/x86_64/)\" "
+ "&& wget -O /tmp/mount-s3.deb "
+ "\"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb\" "
+ "&& sudo apt-get install -y /tmp/mount-s3.deb");
// Mount with Tigris endpoint
String mountPath = "/home/daytona/tigris";
sandbox.getProcess().executeCommand(
"mkdir -p " + mountPath + " && "
+ "mount-s3 --endpoint-url https://t3.storage.dev "
+ "my-tigris-bucket " + mountPath);
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
### Mount a copy-on-write fork per sandbox
A Tigris bucket fork is a new bucket created from a snapshot of a source bucket. The fork shares underlying storage with the source until written to — new writes go only to the fork, and the source bucket and other forks are unaffected. Fork creation is constant-time regardless of source bucket size.
This pattern fits Daytona sandboxes when each sandbox needs a writable copy of a shared dataset (model weights, fixtures, golden data) without duplicating it on every launch.
**Prerequisite** — the source bucket must be created with snapshots enabled. This is a one-time setup, done outside the per-sandbox flow:
```typescript
import { createBucket } from '@tigrisdata/storage'
await createBucket('my-source-bucket', { enableSnapshot: true })
```
In any S3 SDK, send a `CreateBucket` request with the header `X-Tigris-Enable-Snapshot: true`.
The `@tigrisdata/agent-kit` package wraps this workflow as `createForks()`. It snapshots the source bucket and creates one or more forks in a single call. Passing `credentials: { role: 'Editor' }` also creates a scoped access key per fork, so each sandbox can read and write only its own fork bucket instead of the full account.
```typescript
import { createForks, teardownForks } from '@tigrisdata/agent-kit'
import { Daytona } from '@daytona/sdk'
const SOURCE_BUCKET = 'my-source-bucket'
// 1. Snapshot the source and create a fork with a scoped access key
const { data: forkSet, error } = await createForks(SOURCE_BUCKET, 1, {
credentials: { role: 'Editor' },
})
if (error) throw error
const fork = forkSet.forks[0]
// 2. Launch the sandbox with the fork's scoped credentials
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: 'fuse-tigris',
envVars: {
AWS_ACCESS_KEY_ID: fork.credentials!.accessKeyId,
AWS_SECRET_ACCESS_KEY: fork.credentials!.secretAccessKey,
},
})
// 3. Mount the fork bucket
const mountPath = '/home/daytona/tigris'
await sandbox.process.executeCommand(`mkdir -p ${mountPath}`)
await sandbox.process.executeCommand(
`mount-s3 --endpoint-url https://t3.storage.dev ${fork.bucket} ${mountPath}`,
)
try {
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
} finally {
await daytona.delete(sandbox)
await teardownForks(forkSet) // revokes the scoped key and deletes the fork bucket
}
```
To run the same workflow from a language without an `agent-kit` equivalent, use any S3 SDK and send the headers documented below. Send `X-Tigris-Snapshot: true` on `CreateBucket` for the source name, then capture `X-Tigris-Snapshot-Version` from the response. Next, send `CreateBucket` for the fork name with `X-Tigris-Fork-Source-Bucket` and `X-Tigris-Fork-Source-Bucket-Snapshot`. Mount the fork with the same `mount-s3` snippets shown above, replacing the bucket name with the fork bucket.
#### Reference: Tigris-specific headers
These headers drive snapshot and fork operations over the S3 API. Use them with any AWS SDK (boto3, aws-sdk-go-v2, aws-sdk-java-v2, aws-sdk-ruby) by attaching a request interceptor.
| Header | Sent on | Purpose |
| ------------------------------------------ | ----------------------------------------- | ------------------------------------------------------------------------------ |
| **`X-Tigris-Enable-Snapshot: true`** | **`CreateBucket`** (new source) | Enable snapshots on a new bucket. Required before snapshotting it. |
| **`X-Tigris-Snapshot: true`** | **`CreateBucket`** (existing source name) | Take a snapshot of the bucket. Optional **`; name=