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