# Daytona Documentation # https://www.daytona.io/docs # Generated on: 2026-07-18 # 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) — for AI agents 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 agent workflows. Agents and developers interact with sandboxes programmatically using the Daytona SDKs, API, and CLI. Operations span sandbox lifecycle management, filesystem operations, process and code execution, and runtime configuration. Our stateful environment snapshots 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.daytona sdk-java 0.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\")" }' ``` 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")' ``` ## References - **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 API](https://www.daytona.io/docs/en/tools/api.md#daytona) ([OpenAPI spec](https://www.daytona.io/docs/openapi.json)) - [Toolbox API](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox) ([OpenAPI spec](https://www.daytona.io/docs/toolbox-openapi.json)) - [Analytics API](https://www.daytona.io/docs/en/tools/api.md#daytona-analytics) ([OpenAPI spec](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) ```text npx skills add https://github.com/daytona/skills --skill daytona ``` # Architecture Daytona platform is organized into multiple plane components, each serving a specific purpose: - [Interface plane](#interface-plane) provides client interfaces for interacting with Daytona - [Control plane](#control-plane) orchestrates all sandbox operations - [Compute plane](#compute-plane) runs and manages sandbox instances ### Interface plane The interface plane provides client interfaces for users and agents to interact with Daytona. The following components are part of the interface plane and available to all users and agents: - **SDK**: [Python](https://www.daytona.io/docs/en/python-sdk.md), [TypeScript](https://www.daytona.io/docs/en/typescript-sdk.md), [Ruby](https://www.daytona.io/docs/en/ruby-sdk.md), [Go](https://www.daytona.io/docs/en/go-sdk.md), and [Java](https://www.daytona.io/docs/en/java-sdk.md) SDKs for programmatic access - [CLI](https://www.daytona.io/docs/en/tools/cli.md): command-line interface for direct sandbox operations - [Dashboard](https://app.daytona.io/dashboard/): web interface for visual sandbox management and monitoring - [MCP](https://www.daytona.io/docs/en/mcp.md): Model Context Protocol server for AI tool integrations - [SSH](https://www.daytona.io/docs/en/ssh-access.md): secure shell access to running sandboxes ### Control plane The control plane is the central coordination layer of the Daytona platform. It receives all client requests, manages the full sandbox lifecycle, schedules sandboxes onto runners, and continuously reconciles states across the infrastructure. The control plane includes the following components: - [API](#api) handles authentication, sandbox lifecycle management, and resource allocation - [Proxy](#proxy) routes external traffic to sandboxes, enabling direct access to services - [Snapshot builder](#snapshot-builder) builds and manages sandbox [snapshots](https://www.daytona.io/docs/en/snapshots.md) - [Sandbox manager](#sandbox-manager) handles sandbox lifecycle management and state reconciliation #### API The API is a NestJS-based RESTful service that serves as the primary entry point for all platform operations, managing authentication, sandbox lifecycle, snapshots, volumes, and resource allocation. The [snapshot builder](#snapshot-builder) and [sandbox manager](#sandbox-manager) run as internal processes within the API. The API integrates the following internal services and components: - **Redis** provides caching, session management, and distributed locking - **PostgreSQL** serves as the primary persistent store for metadata and configuration - **Auth0/OIDC provider** authenticates users and services via OpenID Connect. The API enforces organization-level multi-tenancy, where each sandbox, snapshot, and volume belongs to an organization, and access control is applied at the organization boundary - **SMTP server** handles email delivery for organization invitations, account notifications, and alert messages - [Sandbox manager](#sandbox-manager) schedules sandboxes onto runners, reconciles states, and enforces sandbox lifecycle management policies - **PostHog** collects platform analytics and usage metrics for monitoring and improvement To interact with sandboxes from the API, see the [API](https://www.daytona.io/docs/en/tools/api.md) and [Toolbox API](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox) references. #### Proxy The proxy is a dedicated HTTP proxy that routes external traffic to the correct sandbox using host-based routing. Each sandbox is reachable at `{port}-{sandboxId}.{proxy-domain}`, where the port maps to a service running inside the sandbox. The proxy resolves the target runner for a given sandbox, injects authentication headers, and forwards the request. It supports both HTTP and WebSocket protocols. #### Snapshot builder The snapshot builder is part of the API process and orchestrates the creation of sandbox [snapshots](https://www.daytona.io/docs/en/snapshots.md) from a Dockerfile or a pre-built image from a [container registry](#container-registry). It coordinates with runners to build or pull images, which are then pushed to an internal snapshot registry that implements the OCI distribution specification. #### Sandbox manager The sandbox manager is part of the API process and schedules sandboxes onto runners, reconciles states, and enforces [sandbox lifecycle management](https://www.daytona.io/docs/en/sandboxes.md#sandbox-lifecycle) policies. ### Compute plane The compute plane is the infrastructure layer where sandboxes run. Sandboxes run on [runners](#sandbox-runners), compute nodes that host multiple sandboxes with dedicated resources and scale horizontally across shared or dedicated [regions](https://www.daytona.io/docs/en/regions.md). The compute plane consists of the following components: - [Sandbox runners](#sandbox-runners) host sandboxes with dedicated resources - [Sandbox daemon](#sandbox-daemon) provides code execution and environment access inside each sandbox - [Snapshot store](#snapshot-store) stores sandbox snapshot images - [Volumes](#volumes) provides persistent storage shared across sandboxes #### Sandbox runners Runners are compute nodes that power Daytona's compute plane, providing the underlying infrastructure for running sandbox workloads. Each runner polls the control plane API for jobs and executes sandbox operations: creating, starting, stopping, destroying, resizing, and backing up sandboxes. Runners interact with S3-compatible object storage for snapshot and volume data, and with the internal snapshot registry. Each sandbox runs as an isolated instance with its own Linux namespaces for processes, network, filesystem mounts, and inter-process communication. Each runner allocates dedicated vCPU, RAM, and disk resources per sandbox. #### Sandbox daemon The sandbox daemon is a code execution agent that runs inside each sandbox. It exposes the [Toolbox API](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox), providing direct access to the sandbox environment: file system and Git operations, process and code execution, computer use, log streaming, and terminal sessions. #### Snapshot store The snapshot store is an internal OCI-compliant registry that stores sandbox snapshot images using the OCI distribution specification. Runners pull snapshot images from this store when creating new sandboxes. The store uses S3-compatible object storage as its backend. #### Volumes [Volumes](https://www.daytona.io/docs/en/volumes.md) provide persistent storage that can be shared across sandboxes. Each volume is backed by S3-compatible object storage and mounted into sandboxes as a read-write directory. Multiple sandboxes can mount the same volume simultaneously, allowing data to be shared across sandboxes and persist independently of the sandbox lifecycle. ### Container registry Container registries serve as the source for sandbox base images. When creating a [snapshot](https://www.daytona.io/docs/en/snapshots.md), the snapshot builder pulls the specified image from an external registry, and pushes it to the internal snapshot registry for use by runners. For Dockerfile-based snapshots, parent images referenced in `FROM` directives are also pulled from the configured source registries during the build. Daytona supports any OCI-compatible registry: - [Docker Hub](https://www.daytona.io/docs/en/snapshots.md#docker-hub) - [Google Artifact Registry](https://www.daytona.io/docs/en/snapshots.md#google-artifact-registry) - [GitHub Container Registry (GHCR)](https://www.daytona.io/docs/en/snapshots.md#github-container-registry) - [Private registries](https://www.daytona.io/docs/en/snapshots.md#using-images-from-private-registries): any registry that implements the OCI distribution specification # 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, and [GPU sandboxes](#gpu-sandboxes) with **NVIDIA GPU** acceleration for model inference, fine-tuning, and CUDA-accelerated compute. ## Create sandboxes Create a sandbox. 1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Click **Create** ```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). [Snapshots](https://www.daytona.io/docs/en/snapshots.md) are persistent captures of sandbox state, including the filesystem, installed packages, and settings. They serve as pre-configured environments for creating sandboxes, and you can [capture them from an existing sandbox](#create-snapshot-from-sandbox) to save its state and restore it later. | **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 **Create Sandbox** 3. Select a **`snapshot`** 4. Click **Create** ```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" }' ``` ### 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 **Create Sandbox** 3. Enter a base **`image`** 4. Set **`resources`** (**`cpu`**, **`memory`**, **`disk`**) to the values within your organization's limits 5. Click **Create** ```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 '{ "image": "ubuntu:22.04", "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`** - **`typescript`** - **`javascript`** ```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 '{ "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 '{ "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 '{ "language": "javascript" }' ``` ## GPU sandboxes Daytona provides **GPU sandboxes** for workloads that require NVIDIA GPU acceleration, such as model inference, fine-tuning, and CUDA-accelerated compute. GPU sandboxes are ephemeral and support up to **16 vCPUs**, **192GB RAM**, and **512GB disk**. Supported GPU types: - **NVIDIA H100** - **NVIDIA H200** - **NVIDIA RTX Pro 6000** - **NVIDIA RTX 4090** - **NVIDIA RTX 5090** > Due to possible events of temporary GPU scarcity, the target/region requested for GPU sandboxes is ignored by default. If you need access to a specific geographical location, contact us at support@daytona.io. Create a GPU sandbox from a default snapshot. 1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Select a **`daytona-gpu`** snapshot 4. Select **`ephemeral`** or set **`auto-delete interval`** to **`0`** 5. Click **Create** ```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 }' ``` Create a GPU sandbox with custom GPU resources: units and types. 1. Create a sandbox from an **`image`** 2. Set the **`auto-delete interval`** to **`0`** (ephemeral) 3. Set the **`GPU`** to the number of GPU units 4. Specify the **`GPU type`**(s): 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. - **`H100`** - **`H200`** - **`RTX-PRO-6000`** - **`RTX-4090`** - **`RTX-5090`** ```python from daytona import Daytona, CreateSandboxFromImageParams, Image, Resources, GpuType daytona = Daytona() sandbox = daytona.create( CreateSandboxFromImageParams( image=Image.debian_slim("3.12"), auto_delete_interval=0, resources=Resources( gpu=1, gpu_type=[GpuType.H100, GpuType.RTX_PRO_6000], ), ) ) ``` ```typescript import { Daytona, GpuType, Image } from "@daytona/sdk"; const daytona = new Daytona() const sandbox = await daytona.create({ image: Image.debianSlim("3.12"), 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: Daytona::Image.debian_slim('3.12'), 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: "python:3.12", 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("python:3.12"); 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 '{ "image": "python:3.12", "autoDeleteInterval": 0, "gpu": 1, "gpuType": ["H100", "RTX-PRO-6000"] }' ``` ## VM sandboxes Daytona provides **VM sandboxes** for workloads that require a full virtual machine with a dedicated **Linux** or **Windows** operating system. VM sandboxes are distinct from container sandboxes and support VM-only capabilities: - [Fork sandboxes](#fork-sandboxes) - [Pause/resume sandboxes](#pause--resume-sandboxes) - [Create snapshot from sandbox](#create-snapshot-from-sandbox) :::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. ::: ### Linux VM Create a Linux VM sandbox. Create a Linux VM sandbox from a default snapshot. 1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Select a Linux VM snapshot: - **`daytona-vm-small`** - **`daytona-vm-medium`** - **`daytona-vm-large`** 4. Click **Create** ```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 Linux VM sandbox from a custom snapshot. 1. Create a snapshot from a base **`image`** 2. Set sandbox class to **`LINUX_VM`** 3. Create a Linux VM sandbox from the snapshot ```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" }' ``` ### Windows Create a Windows sandbox. 1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Select a Windows snapshot: - **`windows-small`** - **`windows-medium`** - **`windows-large`** 4. Click **Create** ```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" }' ``` ## Ephemeral sandboxes Create an ephemeral sandbox. Ephemeral sandboxes are automatically deleted once they are stopped. 1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Set **Ephemeral** to **`True`** or set the [auto-delete interval](#auto-delete-interval) to **`0`** 4. Click **Create** ```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 are attached to an existing parent sandbox at creation time. - **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 }' ``` ## 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' ``` ## 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' ``` ## 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) defer iter.Close() for iter.Next() { sandbox := iter.Value() fmt.Println(sandbox.ID) } if err := iter.Err(); err != nil { log.Fatal(err) } ``` ```java Iterator> iter = daytona.list(); while (iter.hasNext()) { Map sandbox = iter.next(); System.out.println(sandbox.get("id")); } ``` ```bash daytona list [flags] ``` ```bash curl 'https://app.daytona.io/api/sandbox' \ --header 'Authorization: Bearer YOUR_API_KEY' ``` ## Stop sandboxes For [container sandboxes](#create-sandboxes), 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. For [VM sandboxes](#vm-sandboxes) (Linux and Windows), stopping shuts down the virtual machine while preserving the filesystem, and memory state is cleared. To preserve running process state without consuming CPU, use [pause / resume](#pause--resume-sandboxes) instead. 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. 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 moves a stopped container sandbox's filesystem to object storage and free disk quota. Archive is supported for [container sandboxes](#create-sandboxes) only. [VM sandboxes](#vm-sandboxes) (Linux and Windows) do not support archive. Stopping a VM sandbox already offloads filesystem state and releases disk quota, so a separate archive step is not needed. 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' ``` ## Pause / resume sandboxes For [container sandboxes](#create-sandboxes), pause is not supported. The filesystem can be preserved on [stop](#stop-sandboxes), but memory state is not. Use stop to shut down a container sandbox when it is not in use. For [VM sandboxes](#vm-sandboxes) (Linux and Windows), pausing freezes the virtual machine. The filesystem and memory state are preserved, and CPU is no longer consumed. 1. Ensure the VM sandbox is **started** 2. **Pause** the VM sandbox 3. Wait for the VM sandbox to reach the **paused** state 4. **Resume** (start) the 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' ``` ## 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' ``` ## 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 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. 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 # 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 ``` ## Label sandboxes Set sandbox labels. 1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Click **Add Labels** 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" } }' ``` ## Delete sandboxes Delete a sandbox. 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 the **Delete** button 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' ``` ## Create snapshot from sandbox Container sandboxes capture filesystem state only (**cold snapshot**). VM sandboxes capture 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._experimental_create_snapshot("my-snapshot") # Hot snapshot (filesystem and memory, sandbox running) sandbox._experimental_create_snapshot("my-vm-snapshot", include_memory=True) ``` ```typescript // Cold snapshot (filesystem only, sandbox stopped) await sandbox._experimental_createSnapshot('my-snapshot') // Hot snapshot (filesystem and memory, sandbox running) await sandbox._experimental_createSnapshot('my-vm-snapshot', 60, true) ``` ```ruby # Cold snapshot (filesystem only, sandbox stopped) sandbox.experimental_create_snapshot(name: 'my-snapshot') # Hot snapshot (filesystem and memory, sandbox running) sandbox.experimental_create_snapshot(name: 'my-vm-snapshot', include_memory: true) ``` ```go // Cold snapshot (filesystem only, sandbox stopped) err := sandbox.ExperimentalCreateSnapshot(ctx, "my-snapshot") if err != nil { return err } // Hot snapshot (filesystem and memory, sandbox running) err = sandbox.ExperimentalCreateSnapshotWithMemory(ctx, "my-vm-snapshot", 60*time.Second) if err != nil { return err } ``` ```java // Cold snapshot (filesystem only, sandbox stopped) sandbox.experimentalCreateSnapshot("my-snapshot"); // Hot snapshot (filesystem and memory, sandbox running) sandbox.experimentalCreateSnapshot("my-vm-snapshot", 60, true); ``` ```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 }' ``` ## Fork sandboxes Forking is supported for [VM sandboxes](#vm-sandboxes) only. Forking creates a duplicate of a 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 VM sandbox you want to fork 3. Select **Fork** ```python # Fork sandbox through the Sandbox instance forked = sandbox._experimental_fork(name="my-forked-sandbox") ``` ```typescript // Fork sandbox through the Sandbox instance const forkedSandbox = await sandbox._experimental_fork({ name: "my-forked-sandbox" }); // Or use the Daytona helper method const forkedSandbox = await daytona._experimental_fork(sandbox, { name: "my-forked-sandbox" }); ``` ```ruby # Fork sandbox through the Sandbox instance forkedSandbox = sandbox.experimental_fork(name: "my-forked-sandbox") ``` ```go // Fork sandbox through the Sandbox instance name := "my-forked-sandbox" forkedSandbox, err := sandbox.ExperimentalFork(ctx, &name) if err != nil { return err } ``` ```java // Fork sandbox through the Sandbox instance Sandbox forkedSandbox = sandbox.experimentalFork("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' ``` ## Sandbox lifecycle | **Lifecycle feature** | **Container** | **Linux VM** | **Windows** | **GPU** | | ------------------------------------------------- | ------------- | ------------ | ----------- | ------- | | Start sandboxes | ✓ | ✓ | ✓ | ✓ | | Stop sandboxes | ✓ | ✓ | ✓ | ✓ | | Pause / resume sandboxes | ✗ | ✓ | ✓ | ✗ | | Archive sandboxes | ✓ | ✗ | ✗ | ✗ | | Fork sandboxes | ✗ | ✓ | ✓ | ✗ | | Snapshot from sandbox
(filesystem only) | ✓ | ✓ | ✓ | ✓ | | Snapshot from sandbox
(filesystem + memory) | ✗ | ✓ | ✓ | ✗ | A sandbox can have several different states. Each state reflects the status of your sandbox. | **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. | | 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. | The diagram demonstrates the states and possible transitions between them. ##### State transitions 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. | ## Automated lifecycle management Sandboxes can be managed automatically based on user-defined intervals. The intervals act as a TTL (time-to-live) mechanism for the sandbox. - **[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 - **[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 interval 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 **Create Sandbox** 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 **Create** ```python sandbox = daytona.create(CreateSandboxFromSnapshotParams( snapshot="my-snapshot-name", # Disables the auto-stop feature - default is 15 minutes auto_stop_interval=0, )) ``` ```typescript const sandbox = await daytona.create({ snapshot: 'my-snapshot-name', // Disables the auto-stop feature - default is 15 minutes autoStopInterval: 0, }) ``` ```ruby sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'my-snapshot-name', # 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-name", 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-name"); // 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 interval 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) (Linux and Windows) 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. ```python 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' ``` ### Auto-archive interval 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 **Create Sandbox** 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 **Create** ```python sandbox = daytona.create(CreateSandboxFromSnapshotParams( snapshot="my-snapshot-name", # Auto-archive after a sandbox has been stopped for 1 hour auto_archive_interval=60, )) ``` ```typescript const sandbox = await daytona.create({ snapshot: 'my-snapshot-name', // Auto-archive after a sandbox has been stopped for 1 hour autoArchiveInterval: 60, }) ``` ```ruby sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'my-snapshot-name', # 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-name", 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-name"); // 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 interval 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 **Create Sandbox** 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 **Create** ```python sandbox = daytona.create(CreateSandboxFromSnapshotParams( snapshot="my-snapshot-name", # 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-name', // 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-name', # 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-name", 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-name"); // 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' ``` ### 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, set the auto-stop interval to `0` when creating a new sandbox. 1. Go to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Set **`auto-stop`** to **`0`** 4. Click **Create** ```python 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); } } } ``` # 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) | ## 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._experimental_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._experimental_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._experimental_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, )) # Hot snapshot captures the filesystem and memory of the running VM sandbox._experimental_create_snapshot("my-vm-snapshot", include_memory=True) # Sandboxes created from the hot snapshot start with the process already running 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._experimental_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")) # Hot snapshot captures the filesystem and memory of the running VM sandbox._experimental_create_snapshot("my-windows-snapshot", include_memory=True) # Sandboxes created from the hot snapshot start with processes already running 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._experimental_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 (Linux VM and Windows) 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 | ## 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) ``` # 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 your own images or capture the state of existing sandboxes to create snapshots: - [**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**), and VM sandboxes (Linux VM and Windows) capture filesystem and memory state (**hot snapshots**) ## 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 **Create Sandbox** 3. Select a **`snapshot`** 4. Click **Create** ```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" }' ``` ## Create snapshots Create a snapshot. 1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots) 2. Click **Create Snapshot** 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 **Create** ```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. 1. Create a snapshot from a base **`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#windows). 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). 1. Go to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots) 2. Click **Create Snapshot** 3. Enter the snapshot **`name`** and **`image`** 4. Select the **`Allocate GPU`** checkbox 5. Specify the **`GPU type`**(s): - **`NVIDIA H100`** - **`NVIDIA H200`** - **`NVIDIA RTX PRO 6000`** - **`NVIDIA RTX 4090`** - **`NVIDIA RTX 5090`** 6. Click **Create** ```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 Container sandboxes capture filesystem state only (**cold snapshot**). VM sandboxes capture 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._experimental_create_snapshot("my-snapshot") # Hot snapshot (filesystem and memory, sandbox running) sandbox._experimental_create_snapshot("my-vm-snapshot", include_memory=True) ``` ```typescript // Cold snapshot (filesystem only, sandbox stopped) await sandbox._experimental_createSnapshot('my-snapshot') // Hot snapshot (filesystem and memory, sandbox running) await sandbox._experimental_createSnapshot('my-vm-snapshot', 60, true) ``` ```ruby # Cold snapshot (filesystem only, sandbox stopped) sandbox.experimental_create_snapshot(name: 'my-snapshot') # Hot snapshot (filesystem and memory, sandbox running) sandbox.experimental_create_snapshot(name: 'my-vm-snapshot', include_memory: true) ``` ```go // Cold snapshot (filesystem only, sandbox stopped) err := sandbox.ExperimentalCreateSnapshot(ctx, "my-snapshot") if err != nil { return err } // Hot snapshot (filesystem and memory, sandbox running) err = sandbox.ExperimentalCreateSnapshotWithMemory(ctx, "my-vm-snapshot", 60*time.Second) if err != nil { return err } ``` ```java // Cold snapshot (filesystem only, sandbox stopped) sandbox.experimentalCreateSnapshot("my-snapshot"); // Hot snapshot (filesystem and memory, sandbox running) sandbox.experimentalCreateSnapshot("my-vm-snapshot", 60, true); ``` ```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 **Add Registry** 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 **Create Snapshot** 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 **Add Registry** 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 **Add Registry** 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 **Add Registry** 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 **Add Registry** 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 **Create Snapshot** 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 ``` ## Get a snapshot by name 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.Snapshots.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.Snapshots.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 the **Activate** button ```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. 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 the **Deactivate** button ## Delete snapshots Delete a snapshot. Deleted snapshots cannot be recovered. 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 the **Delete** button ```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.Snapshots.Get(ctx, "my-awesome-snapshot") err = client.Snapshots.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. - **Pending**: the snapshot creation has been requested - **Building**: the snapshot is being built - **Pulling**: the snapshot image is being pulled from a registry - **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 Default snapshots include pre-installed Python and Node.js packages. | **Package** | **Version** | | ---------------------- | ----------- | | **`anthropic`** | v0.76.0 | | **`beautifulsoup4`** | v4.14.3 | | **`claude-agent-sdk`** | v0.1.22 | | **`openai-agents`** | v0.15.1 | | **`daytona`** | v0.134.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.33.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.19 | | **`@openai/codex`** | v0.128.0 | | **`bun`** | v1.3.6 | | **`openclaw`** | v2026.2.1 | | **`opencode-ai`** | v1.1.35 | | **`ts-node`** | v10.9.2 | | **`typescript`** | v5.9.3 | | **`typescript-language-server`** | v5.1.3 | ## 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/daytonaio/sdk-go/daytona" "github.com/daytonaio/sdk-go/types" ) func main() { ctx := context.Background() // Initialize the Daytona client client, _ := daytona.NewDaytona(nil) // Create a sandbox from a Docker-in-Docker snapshot sandbox, _ := client.Create(ctx, &types.CreateSandboxFromSnapshotParams{ Snapshot: daytona.Ptr("docker-dind"), }, nil) // Create a docker-compose.yml file composeContent := ` services: web: image: nginx:alpine ports: - "8080:80" ` sandbox.Fs.UploadFile(ctx, []byte(composeContent), "docker-compose.yml") // Start Docker Compose services result, _ := sandbox.Process.ExecuteCommand(ctx, "docker compose -p demo up -d", nil) fmt.Println(result.Result) // Check running services result, _ = sandbox.Process.ExecuteCommand(ctx, "docker compose -p demo ps", nil) fmt.Println(result.Result) // Clean up sandbox.Process.ExecuteCommand(ctx, "docker compose -p demo down", nil) } ``` ## 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. The snippet installs and starts a k3s cluster inside a sandbox and lists all running pods: ```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) ``` # 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) ## 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. ```python # 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 // 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 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). ```python # 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 // 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); ``` ## 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 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 // 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 .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"); ``` ### Dockerfile integration Integrate Dockerfiles and custom Dockerfile commands. ```python # 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 // Add custom Dockerfile commands const image = Image.debianSlim('3.12').dockerfileCommands(['RUN echo "Hello, world!"']) // Use an existing Dockerfile const image = Image.fromDockerfile('Dockerfile') // Extend an existing Dockerfile const 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 the **Create Volume** button 3. Enter the volume name ```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. ```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, Google Cloud Storage, Azure Blob) and filesystems like Archil and MesaFS 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, GCS, Azure Blob, Archil, MesaFS). 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`) 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=