# Daytona Documentation # https://www.daytona.io/docs # Generated on: 2026-06-16 # Daytona Documentation Daytona is an open-source, secure and elastic infrastructure for running AI-generated code. Daytona provides full composable computers — [sandboxes](https://www.daytona.io/docs/en/sandboxes.md) — with complete isolation, a dedicated kernel, filesystem, network stack, and allocated vCPU, RAM, and disk. Sandboxes are the core component of the Daytona platform, spinning up in under 90ms from code to execution and running any code in Python, TypeScript, and JavaScript. Built on OCI/Docker compatibility, massive parallelization, and unlimited persistence, sandboxes deliver consistent, predictable environments for agent workflows. Agents and developers interact with sandboxes programmatically using the Daytona SDKs, API, and CLI. Operations span sandbox lifecycle management, filesystem operations, process and code execution, and runtime configuration. Our stateful environment snapshots enable persistent agent operations across sessions, making Daytona the ideal foundation for AI agent architectures. - **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 API**: [RESTful API](https://www.daytona.io/docs/en/tools/api.md#daytona) ([OpenAPI spec](https://www.daytona.io/docs/openapi.json)), [Toolbox API](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox) ([OpenAPI spec](https://www.daytona.io/docs/toolbox-openapi.json)) - **Daytona CLI**: [Mac/Linux](https://www.daytona.io/docs/en/getting-started.md#cli), [Windows](https://www.daytona.io/docs/en/getting-started.md#cli), [Reference](https://www.daytona.io/docs/en/tools/cli.md) :::tip For faster development with AI agents and assistants, use our LLMs context files: [llms.txt](https://www.daytona.io/docs/llms.txt) and [llms-full.txt](https://www.daytona.io/docs/llms-full.txt), [agent skills](https://github.com/daytona/skills), and markdown pages by appending `.md` to URLs.   ```text npx skills add https://github.com/daytona/skills --skill daytona ``` ::: ## 1. Create an account Open the [Daytona Dashboard ↗](https://app.daytona.io/) to create your account. Daytona supports account creation using an email and password, or by connecting your Google or GitHub account. ## 2. Obtain an API key Generate an API key from the [Daytona Dashboard ↗](https://app.daytona.io/dashboard/keys) or using the [Daytona API](https://www.daytona.io/docs/en/tools/api.md#daytona/tag/api-keys/POST/api-keys) to authenticate SDK requests and access Daytona services. Daytona supports multiple options to configure your environment and API keys: [in code](https://www.daytona.io/docs/en/configuration.md#configuration-in-code), [environment variables](https://www.daytona.io/docs/en/configuration.md#environment-variables), [.env file](https://www.daytona.io/docs/en/configuration.md#env-file), and [default values](https://www.daytona.io/docs/en/configuration.md#default-values). ## 3. Install the SDK Install the Daytona **Python**, **TypeScript**, **Ruby**, **Go**, or **Java** SDKs to interact with sandboxes. ```bash pip install daytona ``` ```bash npm install @daytona/sdk ``` ```bash gem install daytona ``` ```bash go get github.com/daytonaio/daytona/libs/sdk-go ``` **Gradle** Add the Daytona SDK dependency to your `build.gradle.kts`: ```kotlin dependencies { implementation("io.daytona:sdk:x.y.z") } ``` **Maven** Add the Daytona SDK dependency to your `pom.xml`: ```xml io.daytona sdk x.y.z ``` ## 4. Create a Sandbox Create a sandbox to run your code securely in an isolated environment. ```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() ``` ```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() ``` ```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 ``` ```go package main import ( "context" "fmt" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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) fmt.Println(sandbox.ID) } ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Sandbox; import io.daytona.sdk.DaytonaConfig; 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(); System.out.println(sandbox.getId()); } } } ``` ```bash curl https://app.daytona.io/api/sandbox \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{}' ``` ```shell daytona create ``` ## 5. Write and run code Create a program that runs code inside a sandbox. The following snippets are examples of "Hello World" programs that run securely inside a sandbox. ```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 the code securely inside the Sandbox response = sandbox.process.code_run('print("Hello World")') # Check the response if response.exit_code != 0: print(f"Error: {response.exit_code} {response.result}") else: print(response.result) # Clean up sandbox.delete() ``` ```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({ language: 'typescript', }) // Run the code securely inside the Sandbox const response = await sandbox.process.codeRun('console.log("Hello World")') // Check the response if (response.exitCode !== 0) { console.error(`Error: ${response.exitCode} ${response.result}`) } else { console.log(response.result) } // Clean up await sandbox.delete() ``` ```ruby require 'daytona' # Initialize the Daytona client config = Daytona::Config.new(api_key: 'YOUR_API_KEY') daytona = Daytona::Daytona.new(config) # Create the Sandbox instance sandbox = daytona.create # Run the code securely inside the Sandbox response = sandbox.process.code_run(code: 'print("Hello World")') puts response.result ``` ```go // Import the Daytona SDK package main import ( "context" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { // Define the configuration config := &types.DaytonaConfig{ APIKey: "YOUR_API_KEY", // Replace with your API key } // Initialize the Daytona client client, err := daytona.NewClientWithConfig(config) if err != nil { log.Fatal(err) } ctx := context.Background() // Create the Sandbox instance params := types.SnapshotParams{ SandboxBaseParams: types.SandboxBaseParams{ Language: types.CodeLanguagePython, }, } sandbox, err := client.Create(ctx, params) if err != nil { log.Fatal(err) } // Run the code securely inside the Sandbox result, err := sandbox.Process.ExecuteCommand(ctx, `echo "Hello World"`) // Check the response if err != nil { log.Fatalf("Error: %v", err) } if result.ExitCode != 0 { log.Printf("Error: %d %s", result.ExitCode, result.Result) } else { log.Println(result.Result) } // Clean up sandbox.Delete(ctx) } ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.DaytonaConfig; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.ExecuteResponse; public class Main { public static void main(String[] args) { DaytonaConfig config = new DaytonaConfig.Builder() .apiKey("YOUR_API_KEY") // Replace with your API key .build(); try (Daytona daytona = new Daytona(config)) { // Create the Sandbox instance Sandbox sandbox = daytona.create(); // Run the code securely inside the Sandbox ExecuteResponse response = sandbox.getProcess().executeCommand("echo \"Hello World\""); // Check the response if (response.getExitCode() != 0) { System.err.println("Error: " + response.getExitCode() + " " + response.getResult()); } else { System.out.println(response.getResult()); } // Clean up sandbox.delete(); } } } ``` ## Summary By following the steps above, you successfully create a Daytona account, obtain an API key, install the SDK, create a sandbox, write code, and run it securely in a sandbox. ## Next steps Use the following resources to interact with sandboxes: - Learn more about Daytona with the [getting started](https://www.daytona.io/docs/en/getting-started.md) and [sandboxes](https://www.daytona.io/docs/en/sandboxes.md) guides - View [examples](https://www.daytona.io/docs/en/getting-started.md#examples) for common sandbox operations and best practices - Explore [guides](https://www.daytona.io/docs/en/guides.md) to connect Daytona with [Claude](https://www.daytona.io/docs/en/guides/claude.md), [OpenCode](https://www.daytona.io/docs/en/guides/opencode/opencode-web-agent.md), [Codex](https://www.daytona.io/docs/en/guides/codex/codex-sdk-interactive-terminal-sandbox.md), [LangChain](https://www.daytona.io/docs/en/guides/langchain/langchain-data-analysis.md) and more # Architecture Daytona provides **full composable computers** — [sandboxes](https://www.daytona.io/docs/en/sandboxes.md) — for AI agents. Daytona platform is organized into multiple plane components, each serving a specific purpose: - [Interface plane](#interface-plane) provides client interfaces for interacting with Daytona - [Control plane](#control-plane) orchestrates all sandbox operations - [Compute plane](#compute-plane) runs and manages sandbox instances ### Interface plane The interface plane provides client interfaces for users and agents to interact with Daytona. The following components are part of the interface plane and available to all users and agents: - **SDK**: [Python](https://www.daytona.io/docs/en/python-sdk.md), [TypeScript](https://www.daytona.io/docs/en/typescript-sdk.md), [Ruby](https://www.daytona.io/docs/en/ruby-sdk.md), [Go](https://www.daytona.io/docs/en/go-sdk.md), and [Java](https://www.daytona.io/docs/en/java-sdk.md) SDKs for programmatic access - [CLI](https://www.daytona.io/docs/en/tools/cli.md): command-line interface for direct sandbox operations - [Dashboard](https://app.daytona.io/dashboard/): web interface for visual sandbox management and monitoring - [MCP](https://www.daytona.io/docs/en/mcp.md): Model Context Protocol server for AI tool integrations - [SSH](https://www.daytona.io/docs/en/ssh-access.md): secure shell access to running sandboxes ### Control plane The control plane is the central coordination layer of the Daytona platform. It receives all client requests, manages the full sandbox lifecycle, schedules sandboxes onto runners, and continuously reconciles states across the infrastructure. The control plane includes the following components: - [API](#api) handles authentication, sandbox lifecycle management, and resource allocation - [Proxy](#proxy) routes external traffic to sandboxes, enabling direct access to services - [Snapshot builder](#snapshot-builder) builds and manages sandbox [snapshots](https://www.daytona.io/docs/en/snapshots.md) - [Sandbox manager](#sandbox-manager) handles sandbox lifecycle management and state reconciliation #### API The API is a NestJS-based RESTful service that serves as the primary entry point for all platform operations, managing authentication, sandbox lifecycle, snapshots, volumes, and resource allocation. The [snapshot builder](#snapshot-builder) and [sandbox manager](#sandbox-manager) run as internal processes within the API. The API integrates the following internal services and components: - **Redis** provides caching, session management, and distributed locking - **PostgreSQL** serves as the primary persistent store for metadata and configuration - **Auth0/OIDC provider** authenticates users and services via OpenID Connect. The API enforces organization-level multi-tenancy, where each sandbox, snapshot, and volume belongs to an organization, and access control is applied at the organization boundary - **SMTP server** handles email delivery for organization invitations, account notifications, and alert messages - [Sandbox manager](#sandbox-manager) schedules sandboxes onto runners, reconciles states, and enforces sandbox lifecycle management policies - **PostHog** collects platform analytics and usage metrics for monitoring and improvement To interact with sandboxes from the API, see the [API](https://www.daytona.io/docs/en/tools/api.md) and [Toolbox API](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox) references. #### Proxy The proxy is a dedicated HTTP proxy that routes external traffic to the correct sandbox using host-based routing. Each sandbox is reachable at `{port}-{sandboxId}.{proxy-domain}`, where the port maps to a service running inside the sandbox. The proxy resolves the target runner for a given sandbox, injects authentication headers, and forwards the request. It supports both HTTP and WebSocket protocols. #### Snapshot builder The snapshot builder is part of the API process and orchestrates the creation of sandbox [snapshots](https://www.daytona.io/docs/en/snapshots.md) from a Dockerfile or a pre-built image from a [container registry](#container-registry). It coordinates with runners to build or pull images, which are then pushed to an internal snapshot registry that implements the OCI distribution specification. #### Sandbox manager The sandbox manager is part of the API process and schedules sandboxes onto runners, reconciles states, and enforces [sandbox lifecycle management](https://www.daytona.io/docs/en/sandboxes.md#sandbox-lifecycle) policies. ### Compute plane The compute plane is the infrastructure layer where sandboxes run. Sandboxes run on [runners](#sandbox-runners), compute nodes that host multiple sandboxes with dedicated resources and scale horizontally across shared or dedicated [regions](https://www.daytona.io/docs/en/regions.md). The compute plane consists of the following components: - [Sandbox runners](#sandbox-runners) host sandboxes with dedicated resources - [Sandbox daemon](#sandbox-daemon) provides code execution and environment access inside each sandbox - [Snapshot store](#snapshot-store) stores sandbox snapshot images - [Volumes](#volumes) provides persistent storage shared across sandboxes #### Sandbox runners Runners are compute nodes that power Daytona's compute plane, providing the underlying infrastructure for running sandbox workloads. Each runner polls the control plane API for jobs and executes sandbox operations: creating, starting, stopping, destroying, resizing, and backing up sandboxes. Runners interact with S3-compatible object storage for snapshot and volume data, and with the internal snapshot registry. Each sandbox runs as an isolated instance with its own Linux namespaces for processes, network, filesystem mounts, and inter-process communication. Each runner allocates dedicated vCPU, RAM, and disk resources per sandbox. #### Sandbox daemon The sandbox daemon is a code execution agent that runs inside each sandbox. It exposes the [Toolbox API](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox), providing direct access to the sandbox environment: file system and Git operations, process and code execution, computer use, log streaming, and terminal sessions. #### Snapshot store The snapshot store is an internal OCI-compliant registry that stores sandbox snapshot images using the OCI distribution specification. Runners pull snapshot images from this store when creating new sandboxes. The store uses S3-compatible object storage as its backend. #### Volumes [Volumes](https://www.daytona.io/docs/en/volumes.md) provide persistent storage that can be shared across sandboxes. Each volume is backed by S3-compatible object storage and mounted into sandboxes as a read-write directory. Multiple sandboxes can mount the same volume simultaneously, allowing data to be shared across sandboxes and persist independently of the sandbox lifecycle. ### Container registry Container registries serve as the source for sandbox base images. When creating a [snapshot](https://www.daytona.io/docs/en/snapshots.md), the snapshot builder pulls the specified image from an external registry, and pushes it to the internal snapshot registry for use by runners. For Dockerfile-based snapshots, parent images referenced in `FROM` directives are also pulled from the configured source registries during the build. Daytona supports any OCI-compatible registry: - [Docker Hub](https://www.daytona.io/docs/en/snapshots.md#docker-hub) - [Google Artifact Registry](https://www.daytona.io/docs/en/snapshots.md#google-artifact-registry) - [GitHub Container Registry (GHCR)](https://www.daytona.io/docs/en/snapshots.md#github-container-registry) - [Private registries](https://www.daytona.io/docs/en/snapshots.md#using-images-from-private-registries): any registry that implements the OCI distribution specification # Getting Started This section introduces core concepts, common workflows, and next steps for using Daytona. ## Dashboard [Daytona Dashboard ↗](https://app.daytona.io/) is a visual user interface where you can manage sandboxes, access API keys, view usage, and more. It serves as the primary point of control for managing your Daytona resources. ## SDKs Daytona provides [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 to programmatically interact with sandboxes. They support sandbox lifecycle management, code execution, resource access, and more. ## CLI Daytona provides command-line access to core features for interacting with Daytona Sandboxes, including managing their lifecycle, snapshots, and more. To interact with Daytona Sandboxes from the command line, install the Daytona CLI: ```bash brew install daytonaio/cli/daytona ``` Trust the tap once so routine `brew upgrade` keeps the Daytona CLI up to date. Recent Homebrew versions require third-party taps to be explicitly trusted; without it, a bare `brew upgrade` skips the Daytona tap and the CLI goes stale: ```bash brew trust daytonaio/cli ``` To upgrade the Daytona CLI to the latest version: ```bash brew upgrade daytonaio/cli/daytona ``` Alternatively, install directly without Homebrew: For Apple Silicon (`arm64`): ```bash sudo curl -fL https://github.com/daytonaio/daytona/releases/latest/download/daytona-darwin-arm64 -o /usr/local/bin/daytona && sudo chmod +x /usr/local/bin/daytona ``` For Intel (`amd64`): ```bash sudo curl -fL https://github.com/daytonaio/daytona/releases/latest/download/daytona-darwin-amd64 -o /usr/local/bin/daytona && sudo chmod +x /usr/local/bin/daytona ``` Choose the command for your Linux architecture. Both commands download the latest binary from GitHub releases and install it to `/usr/local/bin`, overwriting any existing version. For `amd64` (`x86_64`): ```bash sudo curl -fL https://github.com/daytonaio/daytona/releases/latest/download/daytona-linux-amd64 -o /usr/local/bin/daytona && sudo chmod +x /usr/local/bin/daytona ``` For `arm64` (`aarch64`): ```bash sudo curl -fL https://github.com/daytonaio/daytona/releases/latest/download/daytona-linux-arm64 -o /usr/local/bin/daytona && sudo chmod +x /usr/local/bin/daytona ``` ```bash powershell -Command "irm https://get.daytona.io/windows | iex" ``` After installing the Daytona CLI, use the `daytona` command to interact with Daytona sandboxes from the command line. To view all available commands and flags, see the [CLI reference](https://www.daytona.io/docs/en/tools/cli.md). ## API Daytona provides a RESTful API for interacting with Daytona Sandboxes, including managing their lifecycle, snapshots, and more. It serves as a flexible and powerful way to interact with Daytona from your own applications. To interact with Daytona Sandboxes from the API, see the [API reference](https://www.daytona.io/docs/en/tools/api.md). ## MCP server Daytona provides a Model Context Protocol (MCP) server that enables AI agents to interact with Daytona Sandboxes programmatically. The MCP server integrates with popular AI agents including Claude, Cursor, and Windsurf. To set up the MCP server with your AI agent: ```bash daytona mcp init [claude/cursor/windsurf] ``` For more information, see the [MCP server documentation](https://www.daytona.io/docs/en/mcp.md). ## Multiple runtime support The [TypeScript SDK](https://www.daytona.io/docs/en/typescript-sdk.md) ships as a dual ESM/CJS package and works out of the box in **Node.js**, **Bun**, **Next.js**, **Nuxt.js**, **Remix**, **Vite SSR**, **AWS Lambda**, and **Azure Functions** without any extra configuration. For **Cloudflare Workers**, set the Node.js compatibility flag in your `wrangler.toml`: ```toml compatibility_flags = ["nodejs_compat"] ``` For **Deno**, install with `deno add npm:@daytona/sdk` or import directly with the `npm:` specifier: ```typescript import { Daytona, Image } from 'npm:@daytona/sdk' ``` For **browser apps with Vite** (or any browser bundler), install [`vite-plugin-node-polyfills`](https://www.npmjs.com/package/vite-plugin-node-polyfills) and add it to your `vite.config.ts`: ```typescript import { defineConfig } from 'vite' import { nodePolyfills } from 'vite-plugin-node-polyfills' plugins: [nodePolyfills({ globals: { Buffer: true, process: true, global: true } })], }) ``` The SDK uses Node's `Buffer` for binary data (downloaded files, multipart bodies). Browsers don't ship `Buffer`, so the polyfill provides it. Without it, basic operations like `Image.base()` and `daytona.list()` still work, but methods that handle binary payloads (`fs.downloadFile`, `fs.downloadFiles`) will throw. Some runtimes don't expose the full set of Node.js APIs (browsers and edge runtimes have no filesystem, no `crypto`, etc.). Methods that depend on those APIs throw a clear runtime error instead of silently producing wrong results. ## Guides Daytona provides a comprehensive set of [guides](https://www.daytona.io/docs/en/guides.md) to help you get started. The guides cover a wide range of topics, from basic usage to advanced topics, and showcase various types of integrations between Daytona and other tools. ## Examples Daytona provides quick examples for common sandbox operations and best practices.
The examples are based on the Daytona [Python](https://www.daytona.io/docs/en/python-sdk.md), [TypeScript](https://www.daytona.io/docs/en/typescript-sdk.md), [Go](https://www.daytona.io/docs/en/go-sdk.md), [Ruby](https://www.daytona.io/docs/en/ruby-sdk.md), [Java](https://www.daytona.io/docs/en/java-sdk.md) **SDKs**, [CLI](https://www.daytona.io/docs/en/tools/cli.md), and [API](https://www.daytona.io/docs/en/tools/api.md) references. More examples are available in our [GitHub repository](https://github.com/daytonaio/daytona/tree/main/examples). ### Create a sandbox Create a [sandbox](https://www.daytona.io/docs/en/sandboxes.md) with default settings. ```python from daytona import Daytona daytona = Daytona() sandbox = daytona.create() print(f"Sandbox ID: {sandbox.id}") ``` ```typescript import { Daytona } from '@daytona/sdk'; const daytona = new Daytona(); const sandbox = await daytona.create(); console.log(`Sandbox ID: ${sandbox.id}`); ``` ```go package main import ( "context" "fmt" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" ) func main() { client, err := daytona.NewClient() if err != nil { log.Fatal(err) } sandbox, err := client.Create(context.Background(), nil) if err != nil { log.Fatal(err) } fmt.Printf("Sandbox ID: %s\n", sandbox.ID) } ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create puts "Sandbox ID: #{sandbox.id}" ``` ```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(); System.out.println("Sandbox ID: " + sandbox.getId()); } } } ``` ```shell daytona create ``` ```bash curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{}' ``` ### Create and run code in a sandbox Create a [sandbox](https://www.daytona.io/docs/en/sandboxes.md) and run code securely in it. ```python from daytona import Daytona daytona = Daytona() sandbox = daytona.create() response = sandbox.process.exec("echo 'Hello, World!'") print(response.result) sandbox.delete() ``` ```typescript import { Daytona } from '@daytona/sdk'; const daytona = new Daytona(); const sandbox = await daytona.create(); const response = await sandbox.process.executeCommand('echo "Hello, World!"'); console.log(response.result); await sandbox.delete(); ``` ```go package main import ( "context" "fmt" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" ) func main() { client, err := daytona.NewClient() if err != nil { log.Fatal(err) } sandbox, err := client.Create(context.Background(), nil) if err != nil { log.Fatal(err) } response, err := sandbox.Process.ExecuteCommand(context.Background(), "echo 'Hello, World!'") if err != nil { log.Fatal(err) } fmt.Println(response.Result) sandbox.Delete(context.Background()) } ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create response = sandbox.process.exec(command: "echo 'Hello, World!'") puts response.result daytona.delete(sandbox) ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.ExecuteResponse; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { Sandbox sandbox = daytona.create(); ExecuteResponse response = sandbox.process.executeCommand("echo 'Hello, World!'"); System.out.println(response.getResult()); sandbox.delete(); } } } ``` ```shell daytona create --name my-sandbox daytona exec my-sandbox -- echo 'Hello, World!' daytona delete my-sandbox ``` ```bash # Create a sandbox curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{}' # Execute a command in the sandbox curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/process/execute' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "command": "echo '\''Hello, World!'\''" }' # Delete the sandbox curl 'https://app.daytona.io/api/sandbox/{sandboxId}' \ --request DELETE \ --header 'Authorization: Bearer ' ``` ### Create a sandbox with custom resources Create a sandbox with [custom resources](https://www.daytona.io/docs/en/sandboxes.md#resources) (CPU, memory, disk). ```python from daytona import Daytona, CreateSandboxFromImageParams, Image, Resources daytona = Daytona() sandbox = daytona.create( CreateSandboxFromImageParams( image=Image.debian_slim("3.12"), 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.debianSlim('3.12'), resources: { cpu: 2, memory: 4, disk: 8 } }); ``` ```go package main import ( "context" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, err := daytona.NewClient() if err != nil { log.Fatal(err) } sandbox, err := client.Create(context.Background(), types.ImageParams{ Image: daytona.DebianSlim(nil), Resources: &types.Resources{ CPU: 2, Memory: 4, Disk: 8, }, }) if err != nil { log.Fatal(err) } } ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create( Daytona::CreateSandboxFromImageParams.new( image: Daytona::Image.debian_slim('3.12'), resources: Daytona::Resources.new(cpu: 2, memory: 4, disk: 8) ) ) ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Image; 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(Image.debianSlim("3.12")); Resources resources = new Resources(); resources.setCpu(2); resources.setMemory(4); resources.setDisk(8); params.setResources(resources); Sandbox sandbox = daytona.create(params); } } } ``` ```shell daytona create --class small ``` ```bash curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "cpu": 2, "memory": 4, "disk": 8 }' ``` ### Create an ephemeral sandbox Create an [ephemeral sandbox](https://www.daytona.io/docs/en/sandboxes.md#ephemeral-sandboxes) that is automatically deleted when stopped. ```python from daytona import Daytona, CreateSandboxFromSnapshotParams daytona = Daytona() sandbox = daytona.create( CreateSandboxFromSnapshotParams(ephemeral=True, auto_stop_interval=5) ) ``` ```typescript import { Daytona } from '@daytona/sdk'; const daytona = new Daytona(); const sandbox = await daytona.create({ ephemeral: true, autoStopInterval: 5 }); ``` ```go package main import ( "context" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, err := daytona.NewClient() if err != nil { log.Fatal(err) } autoStop := 5 sandbox, err := client.Create(context.Background(), types.SnapshotParams{ SandboxBaseParams: types.SandboxBaseParams{ Ephemeral: true, AutoStopInterval: &autoStop, }, }) if err != nil { log.Fatal(err) } } ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new(ephemeral: true, auto_stop_interval: 5) ) ``` ```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); // same effect as ephemeral: true params.setAutoStopInterval(5); Sandbox sandbox = daytona.create(params); } } } ``` ```shell daytona create --auto-stop 5 --auto-delete 0 ``` ```bash curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "autoStopInterval": 5, "autoDeleteInterval": 0 }' ``` ### Create a sandbox from a snapshot Create a sandbox from a pre-built [snapshot](https://www.daytona.io/docs/en/snapshots.md) for faster sandbox creation with pre-installed dependencies. ```python from daytona import Daytona, CreateSandboxFromSnapshotParams daytona = Daytona() sandbox = daytona.create( CreateSandboxFromSnapshotParams( snapshot="my-snapshot-name", language="python" ) ) ``` ```typescript import { Daytona } from '@daytona/sdk'; const daytona = new Daytona(); const sandbox = await daytona.create({ snapshot: 'my-snapshot-name', language: 'typescript' }); ``` ```go package main import ( "context" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, err := daytona.NewClient() if err != nil { log.Fatal(err) } sandbox, err := client.Create(context.Background(), types.SnapshotParams{ Snapshot: "my-snapshot-name", SandboxBaseParams: types.SandboxBaseParams{ Language: types.CodeLanguagePython, }, }) if err != nil { log.Fatal(err) } } ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'my-snapshot-name', language: Daytona::CodeLanguage::PYTHON ) ) ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.CreateSandboxFromSnapshotParams; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams(); params.setSnapshot("my-snapshot-name"); params.setLanguage("python"); Sandbox sandbox = daytona.create(params); } } } ``` ```shell daytona create --snapshot my-snapshot-name ``` ```bash curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "snapshot": "my-snapshot-name" }' ``` ### Create a sandbox with a declarative image Create a sandbox with a [declarative image](https://www.daytona.io/docs/en/declarative-builder.md) that defines dependencies programmatically. ```python from daytona import Daytona, CreateSandboxFromImageParams, Image daytona = Daytona() image = ( Image.debian_slim("3.12") .pip_install(["requests", "pandas", "numpy"]) .workdir("/home/daytona") ) sandbox = daytona.create( CreateSandboxFromImageParams(image=image), on_snapshot_create_logs=print ) ``` ```typescript import { Daytona, Image } from '@daytona/sdk'; const daytona = new Daytona(); const image = Image.debianSlim('3.12') .pipInstall(['requests', 'pandas', 'numpy']) .workdir('/home/daytona'); const sandbox = await daytona.create( { image }, { onSnapshotCreateLogs: console.log } ); ``` ```go package main import ( "context" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, err := daytona.NewClient() if err != nil { log.Fatal(err) } image := daytona.DebianSlim(nil). PipInstall([]string{"requests", "pandas", "numpy"}). Workdir("/home/daytona") sandbox, err := client.Create(context.Background(), types.ImageParams{ Image: image, }) if err != nil { log.Fatal(err) } } ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new image = Daytona::Image .debian_slim('3.12') .pip_install(['requests', 'pandas', 'numpy']) .workdir('/home/daytona') sandbox = daytona.create( Daytona::CreateSandboxFromImageParams.new(image: image), on_snapshot_create_logs: proc { |chunk| puts chunk } ) ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Image; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.CreateSandboxFromImageParams; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { Image image = Image.debianSlim("3.12") .pipInstall("requests", "pandas", "numpy") .workdir("/home/daytona"); CreateSandboxFromImageParams params = new CreateSandboxFromImageParams(); params.setImage(image); Sandbox sandbox = daytona.create(params, 60, System.out::println); } } } ``` ```shell daytona create --dockerfile ./Dockerfile ``` ```bash curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "buildInfo": { "dockerfileContent": "FROM python:3.12-slim\nRUN pip install requests pandas numpy\nWORKDIR /home/daytona" } }' ``` ### Create a sandbox with volumes Create a sandbox with a [volume](https://www.daytona.io/docs/en/volumes.md) mounted to share data across sandboxes. ```python from daytona import Daytona, CreateSandboxFromSnapshotParams, VolumeMount daytona = Daytona() volume = daytona.volume.get("my-volume", create=True) sandbox = daytona.create( CreateSandboxFromSnapshotParams( volumes=[VolumeMount(volume_id=volume.id, mount_path="/home/daytona/data")] ) ) ``` ```typescript import { Daytona } from '@daytona/sdk'; const daytona = new Daytona(); const volume = await daytona.volume.get('my-volume', true); const sandbox = await daytona.create({ volumes: [{ volumeId: volume.id, mountPath: '/home/daytona/data' }] }); ``` ```go package main import ( "context" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, err := daytona.NewClient() if err != nil { log.Fatal(err) } volume, err := client.Volume.Get(context.Background(), "my-volume") if err != nil { volume, err = client.Volume.Create(context.Background(), "my-volume") if err != nil { log.Fatal(err) } } sandbox, err := client.Create(context.Background(), types.SnapshotParams{ SandboxBaseParams: types.SandboxBaseParams{ Volumes: []types.VolumeMount{{ VolumeID: volume.ID, MountPath: "/home/daytona/data", }}, }, }) if err != nil { log.Fatal(err) } } ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new volume = daytona.volume.get('my-volume', create: true) sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( volumes: [DaytonaApiClient::SandboxVolume.new( volume_id: volume.id, mount_path: '/home/daytona/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.util.Collections; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { Volume volume; try { volume = daytona.volume().getByName("my-volume"); } catch (DaytonaNotFoundException e) { volume = daytona.volume().create("my-volume"); } VolumeMount mount = new VolumeMount(); mount.setVolumeId(volume.getId()); mount.setMountPath("/home/daytona/data"); CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams(); params.setVolumes(Collections.singletonList(mount)); Sandbox sandbox = daytona.create(params); } } } ``` ```shell daytona volume create my-volume daytona create --volume my-volume:/home/daytona/data ``` ```bash curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "volumes": [ { "volumeId": "", "mountPath": "/home/daytona/data" } ] }' ``` ### Create a sandbox with a Git repository cloned Create a sandbox with a [Git repository](https://www.daytona.io/docs/en/typescript-sdk/git.md) cloned to manage version control. ```python from daytona import Daytona daytona = Daytona() sandbox = daytona.create() sandbox.git.clone("https://github.com/daytonaio/daytona.git", "/home/daytona/daytona") status = sandbox.git.status("/home/daytona/daytona") print(f"Branch: {status.current_branch}") ``` ```typescript import { Daytona } from '@daytona/sdk'; const daytona = new Daytona(); const sandbox = await daytona.create(); await sandbox.git.clone('https://github.com/daytonaio/daytona.git', '/home/daytona/daytona'); const status = await sandbox.git.status('/home/daytona/daytona'); console.log(`Branch: ${status.currentBranch}`); ``` ```go package main import ( "context" "fmt" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" ) func main() { client, err := daytona.NewClient() if err != nil { log.Fatal(err) } sandbox, err := client.Create(context.Background(), nil) if err != nil { log.Fatal(err) } sandbox.Git.Clone(context.Background(), "https://github.com/daytonaio/daytona.git", "/home/daytona/daytona") status, err := sandbox.Git.Status(context.Background(), "/home/daytona/daytona") if err != nil { log.Fatal(err) } fmt.Printf("Branch: %s\n", status.CurrentBranch) } ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create sandbox.git.clone(url: "https://github.com/daytonaio/daytona.git", path: "/home/daytona/daytona") status = sandbox.git.status("/home/daytona/daytona") puts "Branch: #{status.current_branch}" ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.GitStatus; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { Sandbox sandbox = daytona.create(); sandbox.git.clone("https://github.com/daytonaio/daytona.git", "/home/daytona/daytona"); GitStatus status = sandbox.git.status("/home/daytona/daytona"); System.out.println("Branch: " + status.getCurrentBranch()); } } } ``` ```bash # Create a sandbox curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{}' # Clone a Git repository in the sandbox curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/git/clone' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "url": "https://github.com/daytonaio/daytona.git", "path": "/home/daytona/daytona" }' # Get repository status curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/git/status?path=/home/daytona/daytona' \ --header 'Authorization: Bearer ' ``` # 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 get access to a full composable computer where they can install packages, run servers, compile code, and manage processes. 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**. Sandboxes can use [snapshots](https://www.daytona.io/docs/en/snapshots.md) to capture a fully configured environment (base operating system, installed packages, dependencies and configuration) to create new sandboxes. Each sandbox has its own network stack with per-sandbox firewall rules. By default, sandboxes follow standard network policies, but you can restrict egress to a specific set of allowed destinations or block all outbound traffic entirely. - **Sandbox SDKs**: [TypeScript](https://www.daytona.io/docs/en/typescript-sdk/sandbox.md), [Python](https://www.daytona.io/docs/en/python-sdk/sync/sandbox.md), [Ruby](https://www.daytona.io/docs/en/ruby-sdk/sandbox.md), [Go](https://www.daytona.io/docs/en/go-sdk/daytona.md#type-sandbox), [Java](https://www.daytona.io/docs/en/java-sdk/sandbox.md) - **Sandbox API**: [RESTful API](https://www.daytona.io/docs/en/tools/api.md#daytona/tag/sandbox) ([OpenAPI spec](https://www.daytona.io/docs/openapi.json)), [Toolbox API](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox) ([OpenAPI spec](https://www.daytona.io/docs/toolbox-openapi.json)) - **Sandbox CLI**: [Mac/Linux/Windows](https://www.daytona.io/docs/en/tools/cli.md) ## Create Sandboxes Daytona provides methods to create sandboxes. 1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Click **Create** to create a sandbox ```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/daytonaio/daytona/libs/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 '{}' ``` ### GPU Sandboxes Daytona provides methods to create GPU sandboxes. Daytona supports NVIDIA GPU devices for creating GPU sandboxes. Use GPU sandboxes for workloads such as model inference, fine-tuning, and CUDA-accelerated compute. - **NVIDIA H100** - **NVIDIA RTX Pro 6000** Daytona provides a pre-built `daytona-gpu` snapshot for creating GPU sandboxes. Each GPU sandbox is ephemeral and supports up to **16 vCPUs**, **192GB RAM**, and **512GB disk**. 1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Select a GPU snapshot (**`daytona-gpu`**) 4. Select **`us-east-1`** region 5. Click **Create** to create a GPU sandbox ```python from daytona import Daytona, DaytonaConfig, CreateSandboxFromSnapshotParams daytona = Daytona(DaytonaConfig(target="us-east-1")) sandbox = daytona.create( CreateSandboxFromSnapshotParams( snapshot="daytona-gpu", auto_delete_interval=0 ), ) ``` ```typescript import { Daytona } from "@daytona/sdk"; const daytona = new Daytona({ target: "us-east-1", }); const sandbox = await daytona.create({ snapshot: "daytona-gpu", ephemeral: true, }); ``` ```ruby require 'daytona' config = Daytona::Config.new( target: "us-east-1" ) daytona = Daytona::Daytona.new(config) sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'daytona-gpu', ephemeral: true ) ) ``` ```go package main import ( "context" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, _ := daytona.NewClientWithConfig(&types.DaytonaConfig{ Target: "us-east-1", }) 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.DaytonaConfig; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.CreateSandboxFromSnapshotParams; public class App { public static void main(String[] args) { DaytonaConfig config = new DaytonaConfig.Builder() .apiKey(System.getenv("DAYTONA_API_KEY")) .target("us-east-1") .build(); try (Daytona daytona = new Daytona(config)) { CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams(); params.setSnapshot("daytona-gpu"); params.setAutoDeleteInterval(0); Sandbox sandbox = daytona.create(params); } } } ``` ```bash daytona create --snapshot daytona-gpu --target us-east-1 --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 '{ "target": "us-east-1", "snapshot": "daytona-gpu", "autoDeleteInterval": 0 }' ``` To create a GPU sandbox with custom GPU count and types: 1. Create a sandbox from an **`image`** 2. Set the **`auto-delete interval`** to **`0`** (ephemeral) 3. Set the **`GPU`** count to the number of GPUs you want 4. Specify the **`GPU type`**(s): **`H100`** **`RTX-PRO-6000`** The GPU type field accepts a single value or an ordered list of preferred types. ```python from daytona import Daytona, CreateSandboxFromImageParams, Image, Resources, GpuType, DaytonaConfig daytona = Daytona(DaytonaConfig(target="us-east-1")) 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({ target: "us-east-1", }); 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( Daytona::Config.new( target: "us-east-1" ) ) 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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, _ := daytona.NewClientWithConfig(&types.DaytonaConfig{ Target: "us-east-1", }) 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.DaytonaConfig; 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) { DaytonaConfig config = new DaytonaConfig.Builder() .apiKey(System.getenv("DAYTONA_API_KEY")) .target("us-east-1") .build(); try (Daytona daytona = new Daytona(config)) { 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 '{ "target": "us-east-1", "image": "python:3.12", "autoDeleteInterval": 0, "gpu": 1, "gpuType": ["H100", "RTX-PRO-6000"] }' ``` ### Windows Sandboxes Daytona provides methods to create Windows sandboxes. Windows sandboxes are Windows OS runtime environments used to run Windows applications. Use Windows sandboxes to run Windows-specific tools and workflows on a consistent Windows baseline. Daytona provides a pre-built `windows` snapshot for creating Windows sandboxes. The snapshot uses **2 vCPU**, **8GiB** memory, and **30GiB** disk. 1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click **Create Sandbox** 3. Select a Windows snapshot (**`windows`**) 4. Select **`us`** region 5. Click **Create** to create a Windows sandbox ```python from daytona import Daytona, DaytonaConfig, CreateSandboxFromSnapshotParams daytona = Daytona(DaytonaConfig(target="us")) sandbox = daytona.create( CreateSandboxFromSnapshotParams( snapshot="windows", ) ) ``` ```typescript import { Daytona } from "@daytona/sdk"; const daytona = new Daytona({ target: "us", }); const sandbox = await daytona.create({ snapshot: "windows", }); ``` ```ruby require 'daytona' config = Daytona::Config.new( target: "us" ) daytona = Daytona::Daytona.new(config) sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'windows' ) ) ``` ```go package main import ( "context" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, _ := daytona.NewClientWithConfig(&types.DaytonaConfig{ Target: "us", }) ctx := context.Background() params := types.SnapshotParams{ Snapshot: "windows", } _, _ = client.Create(ctx, params) } ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.DaytonaConfig; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.CreateSandboxFromSnapshotParams; 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)) { CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams(); params.setSnapshot("windows"); Sandbox sandbox = daytona.create(params); } } } ``` ```bash daytona create --snapshot windows --target us ``` ```bash curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "target": "us", "snapshot": "windows" }' ``` ### Android Sandboxes **Available in the `android` region. Contact [support@daytona.io](mailto:support@daytona.io) to request access.** Daytona provides methods to create Android sandboxes. Android sandboxes are Android emulator runtime environments used to run Android applications. Use Android sandboxes to run Android-specific tools and workflows on a consistent Android baseline. An Android setup pairs a standard Linux sandbox with one or more emulator devices: - **Base sandbox**: a standard Linux sandbox where your code runs (git, builds, POSIX tooling) - **Device sandbox(es)**: one or more Android emulator devices, attached as [linked sandboxes](#linked-sandboxes) Daytona provides pre-built Android snapshots for creating Android sandboxes. The snapshots use **4 vCPU**, **8GiB** memory, and **30GiB** disk. - **`android-12`**, **`android-13`**, **`android-14`**, **`android-15`**, **`android-16`** ```python from daytona import ( CreateSandboxFromImageParams, CreateSandboxFromSnapshotParams, Daytona, DaytonaConfig, Image, ) daytona = Daytona(DaytonaConfig(target="android")) # Base sandbox: a Linux machine with adb installed base = daytona.create( CreateSandboxFromImageParams( image=Image.base("ubuntu:24.04").run_commands( "apt-get update && apt-get install -y --no-install-recommends curl unzip ca-certificates && rm -rf /var/lib/apt/lists/*", "curl -fsSL -o /tmp/pt.zip https://dl.google.com/android/repository/platform-tools-latest-linux.zip && unzip -q /tmp/pt.zip -d /opt && rm /tmp/pt.zip", "ln -s /opt/platform-tools/adb /usr/local/bin/adb", ), ) ) # Device sandbox: an Android 16 emulator linked to the base device = daytona.create( CreateSandboxFromSnapshotParams( snapshot="android-16", name="android-emulator", linked_sandbox=base.id, ephemeral=True, ), timeout=600, ) # Drive the device from the base over the link network base.process.exec(f"adb connect {device.name}:5555") response = base.process.exec( f"adb -s {device.name}:5555 shell getprop ro.build.version.release" ) ``` ```typescript import { Daytona, Image } from "@daytona/sdk"; const daytona = new Daytona({ target: "android" }); // Base sandbox: a Linux machine with adb installed const base = await daytona.create({ image: Image.base("ubuntu:24.04").runCommands( "apt-get update && apt-get install -y --no-install-recommends curl unzip ca-certificates && rm -rf /var/lib/apt/lists/*", "curl -fsSL -o /tmp/pt.zip https://dl.google.com/android/repository/platform-tools-latest-linux.zip && unzip -q /tmp/pt.zip -d /opt && rm /tmp/pt.zip", "ln -s /opt/platform-tools/adb /usr/local/bin/adb", ), }); // Device sandbox: an Android 16 emulator linked to the base const device = await daytona.create( { snapshot: "android-16", name: "android-emulator", linkedSandbox: base.id, ephemeral: true, }, { timeout: 600 }, ); // Drive the device from the base over the link network await base.process.executeCommand(`adb connect ${device.name}:5555`); const response = await base.process.executeCommand( `adb -s ${device.name}:5555 shell getprop ro.build.version.release`, ); ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new(Daytona::Config.new(target: 'android')) # Base sandbox: a Linux machine with adb installed base = daytona.create( Daytona::CreateSandboxFromImageParams.new( image: Daytona::Image.base('ubuntu:24.04').run_commands( 'apt-get update && apt-get install -y --no-install-recommends curl unzip ca-certificates && rm -rf /var/lib/apt/lists/*', 'curl -fsSL -o /tmp/pt.zip https://dl.google.com/android/repository/platform-tools-latest-linux.zip && unzip -q /tmp/pt.zip -d /opt && rm /tmp/pt.zip', 'ln -s /opt/platform-tools/adb /usr/local/bin/adb' ) ) ) # Device sandbox: an Android 16 emulator linked to the base device = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'android-16', name: 'android-emulator', linked_sandbox: base.id, ephemeral: true, timeout: 600 ) ) # Drive the device from the base over the link network, # addressing it by the name set at creation base.process.exec(command: 'adb connect android-emulator:5555') response = base.process.exec( command: 'adb -s android-emulator:5555 shell getprop ro.build.version.release' ) ``` ```go package main import ( "context" "fmt" "time" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/options" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, _ := daytona.NewClientWithConfig(&types.DaytonaConfig{Target: "android"}) ctx := context.Background() // Base sandbox: a Linux machine with adb installed base, _ := client.Create(ctx, types.ImageParams{ Image: daytona.Base("ubuntu:24.04"). Run("apt-get update && apt-get install -y --no-install-recommends curl unzip ca-certificates && rm -rf /var/lib/apt/lists/*"). Run("curl -fsSL -o /tmp/pt.zip https://dl.google.com/android/repository/platform-tools-latest-linux.zip && unzip -q /tmp/pt.zip -d /opt && rm /tmp/pt.zip"). Run("ln -s /opt/platform-tools/adb /usr/local/bin/adb"), }) // Device sandbox: an Android 16 emulator linked to the base device, _ := client.Create(ctx, types.SnapshotParams{ Snapshot: "android-16", SandboxBaseParams: types.SandboxBaseParams{ Name: "android-emulator", LinkedSandbox: base.ID, Ephemeral: true, }, }, options.WithTimeout(10*time.Minute)) // Drive the device from the base over the link network _, _ = base.Process.ExecuteCommand(ctx, fmt.Sprintf("adb connect %s:5555", device.Name)) response, _ := base.Process.ExecuteCommand(ctx, fmt.Sprintf("adb -s %s:5555 shell getprop ro.build.version.release", device.Name)) fmt.Println(response.Result) } ``` ```bash # Create the base sandbox (a Linux machine with adb installed) curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "target": "android", "buildInfo": { "dockerfileContent": "FROM ubuntu:24.04\nRUN apt-get update && apt-get install -y --no-install-recommends curl unzip ca-certificates && rm -rf /var/lib/apt/lists/*\nRUN curl -fsSL -o /tmp/pt.zip https://dl.google.com/android/repository/platform-tools-latest-linux.zip && unzip -q /tmp/pt.zip -d /opt && rm /tmp/pt.zip\nRUN ln -s /opt/platform-tools/adb /usr/local/bin/adb\n" } }' # Create the device sandbox curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "target": "android", "snapshot": "android-16", "name": "android-emulator", "linkedSandbox": "BASE_SANDBOX_ID", "autoDeleteInterval": 0 }' ``` ##### SSH port forwarding For connecting tools like Android Studio from your machine, tunnel through the base sandbox, not the device. In the following snippet, the device appears as `localhost:5555`: ```bash ssh -L 5555:LINKED_SANDBOX_ID:5555 SSH_ACCESS_TOKEN@ssh.app.daytona.io ``` ##### Customization rules - **Custom device snapshots**: Available on request. Device images are built by Daytona from a specification you provide. Contact [support@daytona.io](mailto:support@daytona.io) for more information. ### Linked Sandboxes Daytona provides methods to create linked sandboxes. Linked sandboxes are attached to an existing parent sandbox at creation time. Create the parent sandbox first, then create one or more children whose create request references 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. - **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. ```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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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 }' ``` ### Ephemeral Sandboxes Daytona provides methods to create ephemeral sandboxes. Ephemeral sandboxes are automatically deleted once they are stopped. They are useful for short-lived tasks or testing purposes. To create an ephemeral sandbox, set the `ephemeral` parameter to `True` when creating a sandbox. Setting [**`autoDeleteInterval: 0`**](#auto-delete-interval) (ephemeral) has the same effect. ```python from daytona import Daytona, CreateSandboxFromSnapshotParams daytona = Daytona() params = CreateSandboxFromSnapshotParams( ephemeral=True, auto_stop_interval=5, # delete after 5 minutes of inactivity ) sandbox = daytona.create(params) ``` ```typescript import { Daytona } from "@daytona/sdk"; const daytona = new Daytona(); const sandbox = await daytona.create({ ephemeral: true, autoStopInterval: 5, // delete after 5 minutes of inactivity }); ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new params = Daytona::CreateSandboxFromSnapshotParams.new( ephemeral: true, auto_stop_interval: 5 # delete after 5 minutes of inactivity ) sandbox = daytona.create(params) ``` ```go package main import ( "context" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, _ := daytona.NewClient() ctx := context.Background() autoStopInterval := 5 // delete after 5 minutes of inactivity 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); // same effect as ephemeral: true params.setAutoStopInterval(5); // delete after 5 minutes of inactivity Sandbox sandbox = daytona.create(params); } } } ``` ```bash # --auto-delete 0 has the same effect as ephemeral: true 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 }' ``` ### 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`** | ##### Pre-built snapshots Daytona provides [pre-built snapshots](https://www.daytona.io/docs/en/snapshots.md#default-snapshots) with fixed resource sizes for creating sandboxes. | **Snapshot** | **vCPU** | **Memory** | **Storage** | **GPU** | | -------------------- | -------- | ---------- | ----------- | ------- | | **`daytona-small`** | 1 | 1GiB | 3GiB | | | **`daytona-medium`** | 2 | 4GiB | 8GiB | | | **`daytona-large`** | 4 | 8GiB | 10GiB | | | **`daytona-gpu`** | 1 | 1GiB | 1GiB | 1 | | **`windows`** | 2 | 8GiB | 30GiB | | ```python from daytona import Daytona, CreateSandboxFromSnapshotParams daytona = Daytona() sandbox = daytona.create( CreateSandboxFromSnapshotParams( snapshot="daytona-medium", ) ) ``` ```typescript import { Daytona } from "@daytona/sdk"; const daytona = new Daytona(); const sandbox = await daytona.create({ snapshot: "daytona-medium", }); ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'daytona-medium' ) ) ``` ```go package main import ( "context" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, _ := daytona.NewClient() ctx := context.Background() params := types.SnapshotParams{ Snapshot: "daytona-medium", } _, _ = 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-medium"); Sandbox sandbox = daytona.create(params); } } } ``` ```bash daytona create --snapshot daytona-medium ``` ```bash curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "snapshot": "daytona-medium" }' ``` ##### Custom resources Daytona provides methods to create sandboxes with custom resources. Use the `Resources` class to set custom sandbox resources. All resource parameters are optional and must be integers. If not specified, Daytona will use the default values. Maximum values are per-sandbox limits set at the organization level. ```python from daytona import Daytona, CreateSandboxFromImageParams, Image, Resources daytona = Daytona() sandbox = daytona.create( CreateSandboxFromImageParams( image=Image.debian_slim("3.12"), 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.debianSlim("3.12"), resources: { cpu: 2, memory: 4, disk: 8 }, }); ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new sandbox = daytona.create( Daytona::CreateSandboxFromImageParams.new( image: Daytona::Image.debian_slim('3.12'), resources: Daytona::Resources.new( cpu: 2, memory: 4, disk: 8 ) ) ) ``` ```go package main import ( "context" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, _ := daytona.NewClient() ctx := context.Background() _, _ = client.Create(ctx, types.ImageParams{ Image: "python:3.12", 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("python:3.12"); 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": "python:3.12", "cpu": 2, "memory": 4, "disk": 8 }' ``` ## Start Sandboxes Daytona provides methods to start sandboxes. 1. Navigate 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 Daytona provides methods to 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 Daytona provides methods to 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 Daytona provides methods to stop sandboxes. Stopped sandboxes maintain filesystem persistence while their memory state is cleared. They incur only disk usage costs and can be started again when needed. Sandboxes in a stopping or stopped state will no longer accept requests. The stopped state should be used when a sandbox is expected to be started again. Otherwise, it is recommended to stop and then archive the sandbox to eliminate disk usage costs. 1. Navigate 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' ``` 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 hooks. Common use cases for force stop include: - you need to reduce stop time and can accept immediate termination - the entrypoint ignores termination signals or hangs during shutdown ## Pause Sandboxes Daytona provides methods to pause sandboxes. Pausing a sandbox keeps both filesystem state and memory persistence, so sandboxes can resume from in-memory runtime state. Compared to regular stop behavior, pause is useful for workloads with active in-memory context and state continuity. Daytona supports pause functionality through VM-based runners. Pause is handled through the existing stop action. This means stop behaves as pause and preserves memory state, while force stop performs a full shutdown without preserving memory state. ## Archive Sandboxes Daytona provides methods to archive sandboxes. A sandbox must be stopped before it can be archived. When a sandbox is archived, the entire filesystem state is moved to a cost-effective object storage, making it available for an extended period. Starting an archived sandbox takes more time than starting a stopped sandbox, depending on its size. It can be started again in the same way as a stopped sandbox. ```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' ``` ## Recover Sandboxes Daytona provides methods to recover sandboxes. ```python sandbox.recover() ``` ```typescript await sandbox.recover() ``` ```ruby sandbox.recover ``` ```bash curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/recover' \ --request POST \ --header 'Authorization: Bearer YOUR_API_KEY' ``` When a sandbox enters an error state, it can sometimes be recovered using the `recover` method, depending on the underlying error reason. The `recoverable` flag indicates whether the error state can be resolved through an automated recovery procedure. Recovery actions are not performed automatically because they address errors that require **further user intervention**, such as freeing up storage space. ```python # Check if the sandbox is recoverable if sandbox.recoverable: sandbox.recover() ``` ```typescript // Check if the sandbox is recoverable if (sandbox.recoverable) { await sandbox.recover() } ``` ```ruby # Check if the sandbox is in an error state before recovering if sandbox.state == 'error' sandbox.recover end ``` ```bash curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/recover' \ --request POST \ --header 'Authorization: Bearer YOUR_API_KEY' ``` ## Resize Sandboxes Daytona provides methods to resize [sandbox resources](#resources) after creation. 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. Resizing updates the sandbox resource allocation (`cpu`, `memory`, and `disk`) for that sandbox only. CPU and memory control compute capacity for running workloads, while disk controls persistent filesystem capacity. Values must be integers and stay within your organization's per-sandbox resource limits. ```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 ``` ## Fork Sandboxes Daytona provides methods to fork sandboxes. Forking creates a duplicate of your sandbox's filesystem and memory, and copies it into a new sandbox. The new sandbox is fully independent: it can be started, stopped, and deleted without affecting the original. The sandbox must be in started state before forking. Daytona tracks the parent-child relationship in a fork tree, so you can always trace a fork's lineage back to the sandbox it was created from. You can fork a fork, building out branches as needed. The parent sandbox cannot be deleted while it has active fork children. 1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click the three-dot menu (**⋮**) next to the sandbox you want to fork 3. Select **Fork** ```python # Fork sandbox through the Sandbox instance forked = sandbox._experimental_fork(name="my-forked-sandbox") ``` ```typescript // Fork sandbox through the Sandbox instance const forkedSandbox = await sandbox._experimental_fork({ name: "my-forked-sandbox" }); // Or use the Daytona helper method const forkedSandbox = await daytona._experimental_fork(sandbox, { name: "my-forked-sandbox" }); ``` ```ruby # Fork sandbox through the Sandbox instance forkedSandbox = sandbox.experimental_fork(name: "my-forked-sandbox") ``` ```go // Fork sandbox through the Sandbox instance name := "my-forked-sandbox" forkedSandbox, err := sandbox.ExperimentalFork(ctx, &name) if err != nil { return err } ``` ```java // Fork sandbox through the Sandbox instance Sandbox forkedSandbox = sandbox.experimentalFork("my-forked-sandbox", 60); ``` ```bash curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/fork' \ --request POST \ --header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "name": "my-forked-sandbox" }' ``` To view the fork tree for a sandbox and all its related sandboxes: 1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click the three-dot menu (**⋮**) next to a sandbox 3. Select **View Fork Tree** The fork tree displays each sandbox in the hierarchy along with its current state and creation time, allowing you to trace the lineage of any fork back to its origin. ## Label Sandboxes Daytona provides methods to set sandbox labels. Setting labels replaces the full label set for the sandbox. Include all labels you want to keep in the request. If you omit an existing label, it will be removed. ```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" } }' ``` ## Create Snapshot from Sandbox Daytona provides methods to create [snapshots](https://www.daytona.io/docs/en/snapshots.md) from sandboxes. A snapshot captures an immutable, point-in-time copy of a sandbox's filesystem and memory that you can use as a base to create new sandboxes, effectively templating a known-good environment for reuse. You can think of it as a checkpoint you can restore from whenever you need a clean, identical starting point. [Windows sandboxes](#windows-sandboxes) use the `includeMemory` parameter to control whether the snapshot also captures the sandbox's memory. | **Include memory** | **Snapshot contents** | **Required sandbox state** | | ------------------- | ------------------------- | -------------------------- | | **`false`** (default) | Filesystem only | Stopped | | **`true`** | Filesystem and memory | Started | ```python # Create snapshot from sandbox sandbox._experimental_create_snapshot("my-sandbox-snapshot") ``` ```typescript // Create snapshot from sandbox await sandbox._experimental_createSnapshot("my-sandbox-snapshot"); ``` ```ruby # Create snapshot from sandbox sandbox.experimental_create_snapshot(name: "my-sandbox-snapshot") ``` ```go // Create snapshot from sandbox err := sandbox.ExperimentalCreateSnapshot(ctx, "my-sandbox-snapshot") if err != nil { return err } ``` ```java // Create snapshot from sandbox sandbox.experimentalCreateSnapshot("my-sandbox-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-sandbox-snapshot", "includeMemory": false }' ``` ## Delete Sandboxes Daytona provides methods to delete sandboxes. 1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes) 2. Click the **Delete** button next to the sandbox you want to delete. ```python sandbox.delete() ``` ```typescript await sandbox.delete() ``` ```ruby sandbox.delete ``` ```go err = sandbox.Delete(ctx) ``` ```java sandbox.delete(); ``` ```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' ``` ## Sandbox lifecycle A sandbox can have several different states. Each state reflects the status of your sandbox. - [**Creating**](#create-sandboxes): the sandbox is provisioning and will be ready to use - [**Starting**](#start-sandboxes): the sandbox is starting and will be ready to use - [**Started**](#start-sandboxes): the sandbox has started and is ready to use - [**Stopping**](#stop-sandboxes): the sandbox is stopping and will no longer accept requests - [**Stopped**](#stop-sandboxes): the sandbox has stopped and is no longer running - [**Deleting**](#delete-sandboxes): the sandbox is deleting and will be removed - [**Deleted**](#delete-sandboxes): the sandbox has been deleted and no longer exists - [**Archiving**](#archive-sandboxes): the sandbox is archiving and its state will be preserved - [**Archived**](#archive-sandboxes): the sandbox has been archived and its state is preserved - [**Resizing**](#resize-sandboxes): the sandbox is being resized to a new set of resources - [**Error**](#recover-sandboxes): the sandbox is in an error state and needs to be recovered - **Restoring**: the sandbox is being restored from archive and will be ready to use shortly - **Unknown**: the default sandbox state before it is created - **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 - **Build Pending**: the sandbox build is pending and will start shortly - **Build Failed**: the sandbox build failed and needs to be retried The diagram demonstrates the states and possible transitions between them. ## Multiple runtime support 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: - **`python`** - **`typescript`** - **`javascript`** If omitted, the Daytona SDK will default to `python`. To override this, explicitly set the `language` value when creating the sandbox. ## Automated lifecycle management Sandboxes can be automatically stopped, archived, and deleted based on user-defined intervals. The intervals act as a TTL (time-to-live) mechanism for the sandbox. You can also refresh the last activity timestamp to explicitly signal activity when lifecycle behavior depends on inactivity intervals. ### Update sandbox last activity Daytona provides methods to 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. For example, if you run long-lived automation around a sandbox and want to avoid unintended auto-stop behavior, call this operation periodically to indicate that the sandbox is still actively used. ```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' ``` ### Auto-stop interval Daytona provides methods to set the auto-stop interval. The auto-stop interval sets the amount of time after which a running sandbox will be automatically stopped. The auto-stop triggers even if there are internal processes running in the sandbox. 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](#what-resets-the-timer) - [What does not reset the timer](#what-does-not-reset-the-timer) The parameter can either be set to: - a time interval in minutes - `0`: disables the auto-stop functionality, allowing the sandbox to run indefinitely If the parameter is not set, the default interval of `15 minutes` will be used. ```python sandbox = daytona.create(CreateSandboxFromSnapshotParams( snapshot="my-snapshot-name", # Disables the auto-stop feature - default is 15 minutes auto_stop_interval=0, )) ``` ```typescript const sandbox = await daytona.create({ snapshot: 'my-snapshot-name', // Disables the auto-stop feature - default is 15 minutes autoStopInterval: 0, }) ``` ```ruby sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'my-snapshot-name', # Disables the auto-stop feature - default is 15 minutes auto_stop_interval: 0 ) ) ``` ```go // Create a sandbox with auto-stop disabled autoStopInterval := 0 params := types.SnapshotParams{ Snapshot: "my-snapshot-name", SandboxBaseParams: types.SandboxBaseParams{ AutoStopInterval: &autoStopInterval, }, } sandbox, err := client.Create(ctx, params) ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.CreateSandboxFromSnapshotParams; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams(); params.setSnapshot("my-snapshot-name"); // Disables the auto-stop feature - default is 15 minutes params.setAutoStopInterval(0); Sandbox sandbox = daytona.create(params); } } } ``` ```bash curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/autostop/{interval}' \ --request POST \ --header 'Authorization: Bearer YOUR_API_KEY' ``` ##### 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-archive interval Daytona provides methods to set the auto-archive interval. The auto-archive interval sets the amount of time after which a continuously stopped sandbox will be automatically archived. The parameter can either be set to: - a time interval in minutes - `0`: the maximum interval of `30 days` will be used If the parameter is not set, the default interval of `7 days` will be used. ```python sandbox = daytona.create(CreateSandboxFromSnapshotParams( snapshot="my-snapshot-name", # Auto-archive after a sandbox has been stopped for 1 hour auto_archive_interval=60, )) ``` ```typescript const sandbox = await daytona.create({ snapshot: 'my-snapshot-name', // Auto-archive after a sandbox has been stopped for 1 hour autoArchiveInterval: 60, }) ``` ```ruby sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'my-snapshot-name', # Auto-archive after a sandbox has been stopped for 1 hour auto_archive_interval: 60 ) ) ``` ```go // Create a sandbox with auto-archive after 1 hour autoArchiveInterval := 60 params := types.SnapshotParams{ Snapshot: "my-snapshot-name", SandboxBaseParams: types.SandboxBaseParams{ AutoArchiveInterval: &autoArchiveInterval, }, } sandbox, err := client.Create(ctx, params) ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.CreateSandboxFromSnapshotParams; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams(); params.setSnapshot("my-snapshot-name"); // Auto-archive after a sandbox has been stopped for 1 hour params.setAutoArchiveInterval(60); Sandbox sandbox = daytona.create(params); } } } ``` ```bash curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/autoarchive/{interval}' \ --request POST \ --header 'Authorization: Bearer YOUR_API_KEY' ``` ### Auto-delete interval Daytona provides methods to set the auto-delete interval. The auto-delete interval sets the amount of time after which a continuously stopped sandbox will be automatically deleted. By default, sandboxes will never be automatically deleted. The parameter can either be set to: - a time interval in minutes - `-1`: disables the auto-delete functionality - `0`: the sandbox will be deleted immediately after stopping If the parameter is not set, the sandbox will not be deleted automatically. ```python sandbox = daytona.create(CreateSandboxFromSnapshotParams( snapshot="my-snapshot-name", # Auto-delete after a sandbox has been stopped for 1 hour auto_delete_interval=60, )) # Delete the sandbox immediately after it has been stopped sandbox.set_auto_delete_interval(0) # Disable auto-deletion sandbox.set_auto_delete_interval(-1) ``` ```typescript const sandbox = await daytona.create({ snapshot: 'my-snapshot-name', // Auto-delete after a sandbox has been stopped for 1 hour autoDeleteInterval: 60, }) // Delete the sandbox immediately after it has been stopped await sandbox.setAutoDeleteInterval(0) // Disable auto-deletion await sandbox.setAutoDeleteInterval(-1) ``` ```ruby sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new( snapshot: 'my-snapshot-name', # Auto-delete after a sandbox has been stopped for 1 hour auto_delete_interval: 60 ) ) # Delete the sandbox immediately after it has been stopped sandbox.auto_delete_interval = 0 # Disable auto-deletion sandbox.auto_delete_interval = -1 ``` ```go // Create a sandbox with auto-delete after 1 hour autoDeleteInterval := 60 params := types.SnapshotParams{ Snapshot: "my-snapshot-name", SandboxBaseParams: types.SandboxBaseParams{ AutoDeleteInterval: &autoDeleteInterval, }, } sandbox, err := client.Create(ctx, params) // Delete the sandbox immediately after it has been stopped zeroInterval := 0 err = sandbox.SetAutoDeleteInterval(ctx, &zeroInterval) // Disable auto-deletion disableInterval := -1 err = sandbox.SetAutoDeleteInterval(ctx, &disableInterval) ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.Sandbox; import io.daytona.sdk.model.CreateSandboxFromSnapshotParams; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams(); params.setSnapshot("my-snapshot-name"); // Auto-delete after a sandbox has been stopped for 1 hour params.setAutoDeleteInterval(60); Sandbox sandbox = daytona.create(params); // Delete the sandbox immediately after it has been stopped sandbox.setAutoDeleteInterval(0); // Disable auto-deletion sandbox.setAutoDeleteInterval(-1); } } } ``` ```bash curl 'https://app.daytona.io/api/sandbox/{sandboxIdOrName}/autodelete/{interval}' \ --request POST \ --header 'Authorization: Bearer YOUR_API_KEY' ``` ### Running indefinitely Daytona provides methods to run sandboxes indefinitely. By default, Daytona sandboxes auto-stop after 15 minutes of inactivity. To keep a sandbox running without interruption, set the auto-stop interval to `0` when creating a new sandbox: ```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); } } } ``` # Environment Configuration Daytona supports multiple methods to configure your environment, in order of precedence: 1. [Configuration in code](#configuration-in-code) 2. [Environment variables](#environment-variables) 3. [.env file](#env-file) 4. [Default values](#default-values) ## Configuration in code To configure your environment in code, use the `DaytonaConfig` class. The `DaytonaConfig` class accepts the following parameters: - `api_key`: Your Daytona [API Key](https://www.daytona.io/docs/api-keys.md) - `api_url`: URL of your [Daytona API](https://www.daytona.io/docs/en/tools/api.md) - `target`: Target region to create the Sandboxes on (`us` / `eu`) ```python from daytona import DaytonaConfig config = DaytonaConfig( api_key="YOUR_API_KEY", api_url="YOUR_API_URL", target="us" ) ``` ```typescript import { DaytonaConfig } from '@daytona/sdk' const config: DaytonaConfig = { apiKey: 'YOUR_API_KEY', apiUrl: 'YOUR_API_URL', target: 'us', } ``` ```ruby require 'daytona' config = Daytona::Config.new( api_key: 'YOUR_API_KEY', api_url: 'YOUR_API_URL', target: 'us' ) ``` ```go package main import ( "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" ) func main() { config := daytona.Config{ APIKey: "YOUR_API_KEY", APIURL: "YOUR_API_URL", Target: "us", } client := daytona.NewClient(&config) _ = client } ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.DaytonaConfig; public class App { public static void main(String[] args) { DaytonaConfig config = new DaytonaConfig.Builder() .apiKey("YOUR_API_KEY") .apiUrl("YOUR_API_URL") .target("us") .build(); try (Daytona daytona = new Daytona(config)) { // Application code } } } ``` ```bash curl https://app.daytona.io/api/api-keys \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \ --data '{ "name": "", "permissions": [ "write:registries" ], "expiresAt": "" }' ``` ## Environment variables Daytona supports environment variables for configuration. The SDK automatically looks for these environment variables: | Variable | Description | Required | | --------------------- | ------------------------------------------ | -------- | | **`DAYTONA_API_KEY`** | Your Daytona API key. | Yes | | **`DAYTONA_API_URL`** | URL of your Daytona API. | No | | **`DAYTONA_TARGET`** | Daytona Target to create the sandboxes on. | No | ### Shell Set environment variables in your shell using the following methods: ```bash export DAYTONA_API_KEY=your-api-key export DAYTONA_API_URL=https://your-api-url export DAYTONA_TARGET=us ``` ```bash $env:DAYTONA_API_KEY="your-api-key" $env:DAYTONA_API_URL="https://your-api-url" $env:DAYTONA_TARGET="us" ``` ### .env file Set the environment variables in a `.env` file using the following format: ```bash DAYTONA_API_KEY=YOUR_API_KEY DAYTONA_API_URL=https://your_api_url DAYTONA_TARGET=us ``` ## Default values If no configuration is provided, Daytona will use its built-in default values: | **Option** | **Value** | | ---------- | ----------------------------------- | | API URL | https://app.daytona.io/api | | Target | Default region for the organization | # Snapshots Snapshots are reusable sandbox templates built from [Docker](https://www.docker.com/) or [OCI](https://opencontainers.org/) compatible images. Sandboxes can use snapshots to provide a consistent and reproducible environment for your dependencies, settings, and resources. A snapshot defines the base operating system, language runtimes, system packages, and project-level setup that should exist when a sandbox starts. Instead of repeating bootstrap steps on every sandbox creation, you capture that setup once as a snapshot and reuse it. You start with default snapshots for common stacks, or create custom snapshots for your own toolchain and constraints. Custom snapshots are useful when your workflow depends on specific package versions, private dependencies, startup scripts, or filesystem layout. - **Snapshot SDKs**: [TypeScript](https://www.daytona.io/docs/en/typescript-sdk/snapshot.md), [Python](https://www.daytona.io/docs/en/python-sdk/sync/snapshot.md), [Ruby](https://www.daytona.io/docs/en/ruby-sdk/snapshot.md), [Go](https://www.daytona.io/docs/en/go-sdk/daytona.md#type-snapshotservice), [Java](https://www.daytona.io/docs/en/java-sdk/snapshot.md) - **Snapshot API**: [RESTful API](https://www.daytona.io/docs/en/tools/api.md#daytona/tag/snapshots) ([OpenAPI spec](https://www.daytona.io/docs/openapi.json)), [Toolbox API](https://www.daytona.io/docs/en/tools/api.md#daytona-toolbox) ([OpenAPI spec](https://www.daytona.io/docs/toolbox-openapi.json)) - **Snapshot CLI**: [Mac/Linux/Windows](https://www.daytona.io/docs/en/tools/cli.md) ## Create Snapshots Daytona provides methods to create snapshots. You can create a snapshot from: - [GPU snapshots](#gpu-snapshots) (for [GPU sandboxes](https://www.daytona.io/docs/en/sandboxes.md#gpu-sandboxes)) - [public images](#public-images) - [local images](#local-images) - [images from private registries](#images-from-private-registries) - [declarative builder](#declarative-builder) 1. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots) 2. Click **Create Snapshot** 3. Enter the snapshot **`name`** and **`image`** - **Snapshot name**: identifier used to reference the snapshot - **Snapshot image**: base image for the snapshot, must include either a tag or a digest (e.g., **`ubuntu:22.04`**); the `latest`/`lts`/`stable` tags are not supported 4. Click **Create** to create a snapshot ```python from daytona import Daytona, CreateSnapshotParams daytona = Daytona() snapshot = daytona.snapshot.create( CreateSnapshotParams(name="my-awesome-snapshot", image="python:3.12"), ) ``` ```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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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 }' ``` ### GPU Snapshots Daytona provides methods to create GPU snapshots. GPU snapshots are used to create [GPU sandboxes](https://www.daytona.io/docs/en/sandboxes.md#gpu-sandboxes). Daytona provides a pre-built `daytona-gpu` snapshot for creating GPU sandboxes. 1. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots) 2. Click **Create Snapshot** 3. Enter the snapshot **`name`** and **`image`** 4. Select **`us-east-1`** region 5. Select the **`Allocate GPU`** checkbox 6. Click **Create** to create a GPU snapshot ```python from daytona import CreateSnapshotParams, Daytona, DaytonaConfig, Image, Resources daytona = Daytona(DaytonaConfig(target="us-east-1")) 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({ target: "us-east-1" }); 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' config = Daytona::Config.new( target: "us-east-1" ) daytona = Daytona::Daytona.new(config) 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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, _ := daytona.NewClientWithConfig(&types.DaytonaConfig{ Target: "us-east-1", }) 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.DaytonaConfig; 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) { DaytonaConfig config = new DaytonaConfig.Builder() .apiKey(System.getenv("DAYTONA_API_KEY")) .target("us-east-1") .build(); try (Daytona daytona = new Daytona(config)) { 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", "regionId": "us-east-1", "cpu": 1, "memory": 1, "disk": 1, "gpu": 1 }' ``` ### Public images Daytona supports creating snapshots from any publicly accessible image or container registry. 1. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots) 2. Click the **Create Snapshot** button 3. Enter the snapshot **`name`** and **`image`** of any publicly accessible image or container registry ```python from daytona import Daytona, CreateSnapshotParams daytona = Daytona() daytona.snapshot.create( CreateSnapshotParams(name="my-awesome-snapshot", image="python:3.11-slim"), on_logs=lambda chunk: print(chunk, end=""), ) ``` ```typescript import { Daytona } from "@daytona/sdk"; const daytona = new Daytona(); await daytona.snapshot.create( { name: "my-awesome-snapshot", image: "python:3.11-slim" }, { onLogs: console.log }, ); ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new params = Daytona::CreateSnapshotParams.new( name: 'my-awesome-snapshot', image: 'python:3.11-slim' ) snapshot = daytona.snapshot.create(params) do |chunk| print chunk end ``` ```go package main import ( "context" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/sdk-go/pkg/types" ) func main() { client, _ := daytona.NewClient() ctx := context.Background() snapshot, logChan, _ := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{ Name: "my-awesome-snapshot", Image: "python:3.11-slim", }) _ = snapshot for range logChan { } } ``` ```java import io.daytona.sdk.Daytona; import io.daytona.sdk.model.Snapshot; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { Snapshot snapshot = daytona.snapshot().create("my-awesome-snapshot", "python:3.11-slim"); } } } ``` ```bash daytona snapshot create my-awesome-snapshot --image python:3.11-slim ``` ```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" }' ``` ### Local images Daytona supports creating snapshots from local images or from local Dockerfiles. To create a snapshot from a local image or from a local Dockerfile, use the [Daytona CLI](https://www.daytona.io/docs/en/tools/cli.md#daytona-snapshot). 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 ``` ### Images from private registries Daytona supports creating snapshots from images from [Docker Hub](#docker-hub), [Google Artifact Registry](#google-artifact-registry), [GitHub Container Registry](#github-container-registry-ghcr), [Amazon ECR](#amazon-elastic-container-registry-ecr) or other private container registries. 1. Navigate to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries) 2. Click **Add Registry** and select your provider 3. Fill in the visible fields 4. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots) 5. Click **Create Snapshot** 6. Enter the snapshot **`name`** and the full **`image`** reference, including the registry host and repository (e.g. **`my-registry.com//custom-alpine:3.21`**) #### Docker Hub Daytona supports creating snapshots from Docker Hub images. 1. Navigate to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries), 2. Click **Add Registry** and select the **Docker Hub** tab 3. Input the following fields: - **Username**: your Docker Hub username (the account with access to the image) - **Personal Access Token**: a [Docker Hub PAT](https://docs.docker.com/security/access-tokens/) — not your account password - **Registry URL**: auto-filled with **`docker.io`** and not shown in the form 4. Create the snapshot using the full image reference, e.g. **`docker.io//:`** #### Google Artifact Registry Daytona supports creating snapshots from images from Google Artifact Registry, authenticated with a [service account key](https://cloud.google.com/iam/docs/keys-create-delete) in JSON format. 1. Navigate to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries), 2. Click **Add Registry** and select the **Google** tab 2. Input the following fields: - **Registry URL**: the base URL for your region (e.g. **`https://us-central1-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, e.g. **`us-central1-docker.pkg.dev///:`** #### GitHub Container Registry Daytona supports creating snapshots from images from GitHub Container Registry. 1. Navigate to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries), 2. Click **Add Registry** and select the **GitHub** tab 2. Input the following fields: - **GitHub Username**: the account with access to the image - **Personal Access Token**: a [GitHub PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with **`read:packages`** scope (and **`write:packages`** / **`delete:packages`** if you'll push or delete) - **Registry URL**: auto-filled with **`ghcr.io`** and not shown in the form 3. Create the snapshot using the full image reference, e.g. **`ghcr.io//:`** #### 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. No long-lived AWS credentials are shared, and no manual token rotation is needed. You'll need two values: - **Daytona Broker ARN**: `arn:aws:iam::967657494466:role/DaytonaEcrCredentialBroker` — the IAM principal Daytona uses to assume into your role. Self-hosted: substitute the IAM role your API pods assume (e.g. via IRSA). - **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 Create an IAM role with the trust and permissions policies below. Replace `` with your organization ID. 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": "*" }] } ``` Copy the ARN of the role you just created (e.g. `arn:aws:iam::123456789012:role/daytona-ecr-puller`). ##### 2. Register the registry in Daytona 1. On [Daytona Registries ↗](https://app.daytona.io/dashboard/registries), click **Add Registry** and select the **Amazon ECR** tab. 2. Fill in: - **Registry URL**: `.dkr.ecr..amazonaws.com` - **Role ARN**: the role you created in step 1 — Daytona assumes it on every pull 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`. ##### 3. Create the snapshot 1. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots). 2. Click **Create Snapshot**. 3. Enter the snapshot name and the full image reference (e.g. `123456789012.dkr.ecr.us-east-1.amazonaws.com/my-repo/custom-alpine:3.21`). ##### 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--*" } ``` ### Declarative builder [Declarative Builder](https://www.daytona.io/docs/en/declarative-builder.md) 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 [SDKs](https://www.daytona.io/docs/en/getting-started.md#sdks). ### Resources Snapshots can be customized with specific [sandbox resources](https://www.daytona.io/docs/en/sandboxes.md#resources). By default, Daytona sandboxes use **1 vCPU**, **1GB RAM**, and **3GiB disk**. To view your available resources and limits, see [limits](https://www.daytona.io/docs/en/limits.md) or navigate to [Daytona Limits ↗](https://app.daytona.io/dashboard/limits). To set custom snapshot resources, use the `Resources` class. ```python from daytona import Daytona, CreateSnapshotParams, Resources daytona = Daytona() snapshot = daytona.snapshot.create( CreateSnapshotParams( name="my-awesome-snapshot", image="python:3.12", resources=Resources(cpu=2, memory=4, disk=8), ), ) ``` ```typescript import { Daytona } from "@daytona/sdk"; const daytona = new Daytona(); const snapshot = await daytona.snapshot.create({ name: "my-awesome-snapshot1123", image: "python:3.12", resources: { cpu: 2, memory: 4, disk: 8 }, }); ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new snapshot = daytona.snapshot.create( Daytona::CreateSnapshotParams.new( name: 'my-awesome-snapshot', image: 'python:3.12', resources: Daytona::Resources.new( cpu: 2, memory: 4, disk: 8 ) ) ) ``` ```go package main import ( "context" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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", Resources: &types.Resources{ CPU: 2, Memory: 4, Disk: 8, }, }) 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 CreateSnapshotResources { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { Resources resources = new Resources(); resources.setCpu(2); resources.setMemory(4); resources.setDisk(8); Snapshot snapshot = daytona.snapshot().create( "my-awesome-snapshot", Image.base("python:3.12"), resources, null ); } } } ``` ```bash daytona snapshot create my-awesome-snapshot --image python:3.11-slim --cpu 2 --memory 4 --disk 8 ``` ```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, "disk": 8 }' ``` ### Regions When creating a snapshot, you can specify the [region](https://www.daytona.io/docs/en/regions.md) in which it will be available. If not specified, the snapshot will be created in your organization's default region. When you later create a sandbox from this snapshot, you can use the snapshot's region as the target region for the sandbox. ```python from daytona import Daytona, CreateSnapshotParams daytona = Daytona() snapshot = daytona.snapshot.create( CreateSnapshotParams( name="my-awesome-snapshot", image="python:3.12", region_id="eu", ), ) ``` ```typescript import { Daytona } from "@daytona/sdk"; const daytona = new Daytona(); const snapshot = await daytona.snapshot.create({ name: "my-awesome-snapshotus", image: "python:3.12", regionId: "us", }); ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new snapshot = daytona.snapshot.create( Daytona::CreateSnapshotParams.new( name: 'my-awesome-snapshot', image: 'python:3.12', region_id: 'us' ) ) ``` ```bash daytona snapshot create my-awesome-snapshot --image python:3.11-slim --region us ``` ```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", "regionId": "us" }' ``` ## Get a Snapshot by name Daytona provides an option to get a snapshot by name. The following snippet returns the snapshot with the specified 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 Daytona provides options to list snapshots and view their details. The following snippet lists all snapshots on the first page with a limit of 10 snapshots per page. ```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 Snapshots automatically become inactive after 2 weeks of not being used. To activate an inactive snapshot: 1. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots) 2. Click the three dots at the end of the row for the snapshot you want to activate 3. Click the **Activate** button ```python daytona.snapshot.activate("my-awesome-snapshot") ``` ```typescript await daytona.snapshot.activate("my-awesome-snapshot") ``` ```ruby daytona.snapshot.activate('my-awesome-snapshot') ``` ```bash curl https://app.daytona.io/api/snapshots/my-inactive-snapshot/activate \ --request POST \ --header 'Authorization: Bearer YOUR_SECRET_TOKEN' ``` ## Deactivate Snapshots Daytona provides an option to deactivate snapshots. Deactivated snapshots are not available for new sandboxes. 1. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots) 2. Click the three dots at the end of the row for the snapshot you want to deactivate 3. Click the **Deactivate** button ## Delete Snapshots Daytona provides options to delete snapshots. Deleted snapshots cannot be recovered. 1. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots) 2. Click the three dots at the end of the row for the snapshot you want to delete 3. Click the **Delete** button ```python daytona.snapshot.delete(daytona.snapshot.get("my-awesome-snapshot")) ``` ```typescript await daytona.snapshot.delete(await daytona.snapshot.get("my-awesome-snapshot")) ``` ```ruby daytona.snapshot.delete(daytona.snapshot.get('my-awesome-snapshot')) ``` ```go snapshot, err := client.Snapshots.Get(ctx, "my-awesome-snapshot") err = client.Snapshots.Delete(ctx, snapshot) ``` ```java daytona.snapshot().delete(daytona.snapshot().get("my-awesome-snapshot").getId()); ``` ```bash daytona snapshot delete my-awesome-snapshot ``` ```bash curl https://app.daytona.io/api/snapshots/my-awesome-snapshot \ --request DELETE \ --header 'Authorization: Bearer YOUR_SECRET_TOKEN' ``` ## Snapshot lifecycle A snapshot can have several different states. Each state reflects the snapshot's current status. - **Pending**: the snapshot creation has been requested - **Building**: the snapshot is being built - **Pulling**: the snapshot image is being pulled from a registry - **Active**: the snapshot is ready to use for creating sandboxes - **Inactive**: the snapshot is deactivated - **Error**: the snapshot creation failed - **Build Failed**: the snapshot build process failed - **Removing**: the snapshot is being deleted :::note Inactive snapshots cannot be used to create sandboxes. They must be explicitly [re-activated](#activate-snapshots) before use. When activated, the snapshot returns to `pending` state and is re-processed before becoming `active` again. ::: ## Run Docker in a Sandbox Daytona Sandboxes can run Docker containers inside them (**Docker-in-Docker**), enabling you to build, test, and deploy containerized applications. This is particularly useful when your projects have dependencies on external services like databases, message queues, or other microservices. Agents can seamlessly interact with these services since they run within the same sandbox environment, providing better isolation and security compared to external service dependencies. The following use cases are supported: - 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 ##### Using 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 Docker Compose allows you to 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. First, create a Docker-in-Docker snapshot using the [Daytona Dashboard ↗](https://app.daytona.io/dashboard/snapshots) or [CLI](https://www.daytona.io/docs/en/tools/cli.md#daytona-snapshot-create) with one of the [pre-built images](#using-pre-built-images) (e.g., `docker:28.3.3-dind`). Then use the following snippet to run Docker Compose services inside a sandbox: ```python from daytona import Daytona, CreateSandboxFromSnapshotParams # Initialize the Daytona client daytona = Daytona() # Create a sandbox from a Docker-in-Docker snapshot sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot='docker-dind')) # Create a docker-compose.yml file compose_content = ''' services: web: image: nginx:alpine ports: - "8080:80" ''' sandbox.fs.upload_file(compose_content.encode(), 'docker-compose.yml') # Start Docker Compose services result = sandbox.process.exec('docker compose -p demo up -d') print(result.result) # Check running services result = sandbox.process.exec('docker compose -p demo ps') print(result.result) # Clean up sandbox.process.exec('docker compose -p demo down') ``` ```typescript import { Daytona } from '@daytona/sdk' // Initialize the Daytona client const daytona = new Daytona() // Create a sandbox from a Docker-in-Docker snapshot const sandbox = await daytona.create({ snapshot: 'docker-dind' }) // Create a docker-compose.yml file const composeContent = ` services: web: image: nginx:alpine ports: - "8080:80" ` await sandbox.fs.uploadFile(Buffer.from(composeContent), 'docker-compose.yml') // Start Docker Compose services let result = await sandbox.process.executeCommand('docker compose -p demo up -d') console.log(result.result) // Check running services result = await sandbox.process.executeCommand('docker compose -p demo ps') console.log(result.result) // Clean up await sandbox.process.executeCommand('docker compose -p demo down') ``` ```ruby require 'daytona' # Initialize the Daytona client daytona = Daytona::Daytona.new # Create a sandbox from a Docker-in-Docker snapshot sandbox = daytona.create(Daytona::CreateSandboxFromSnapshotParams.new(snapshot: 'docker-dind')) # Create a docker-compose.yml file compose_content = <<~YAML services: web: image: nginx:alpine ports: - "8080:80" YAML sandbox.fs.upload_file(compose_content, 'docker-compose.yml') # Start Docker Compose services result = sandbox.process.exec(command: 'docker compose -p demo up -d') puts result.result # Check running services result = sandbox.process.exec(command: 'docker compose -p demo ps') puts result.result # Clean up sandbox.process.exec(command: 'docker compose -p demo down') ``` ```go package main import ( "context" "fmt" "github.com/daytonaio/sdk-go/daytona" "github.com/daytonaio/sdk-go/types" ) func main() { ctx := context.Background() // Initialize the Daytona client client, _ := daytona.NewDaytona(nil) // Create a sandbox from a Docker-in-Docker snapshot sandbox, _ := client.Create(ctx, &types.CreateSandboxFromSnapshotParams{ Snapshot: daytona.Ptr("docker-dind"), }, nil) // Create a docker-compose.yml file composeContent := ` services: web: image: nginx:alpine ports: - "8080:80" ` sandbox.Fs.UploadFile(ctx, []byte(composeContent), "docker-compose.yml") // Start Docker Compose services result, _ := sandbox.Process.ExecuteCommand(ctx, "docker compose -p demo up -d", nil) fmt.Println(result.Result) // Check running services result, _ = sandbox.Process.ExecuteCommand(ctx, "docker compose -p demo ps", nil) fmt.Println(result.Result) // Clean up sandbox.Process.ExecuteCommand(ctx, "docker compose -p demo down", nil) } ``` ## Run Kubernetes in a Sandbox Daytona 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. ##### Run k3s in a Sandbox The following snippet installs and starts a k3s cluster inside a sandbox and lists all running pods. ```typescript import { Daytona } from '@daytona/sdk' import { setTimeout } from 'timers/promises' // Initialize the Daytona client const daytona = new Daytona() // Create the sandbox instance const sandbox = await daytona.create() // Run the k3s installation script const response = await sandbox.process.executeCommand( 'curl -sfL https://get.k3s.io | sh -' ) // Run k3s const sessionName = 'k3s-server' await sandbox.process.createSession(sessionName) const k3s = await sandbox.process.executeSessionCommand(sessionName, { command: 'sudo /usr/local/bin/k3s server', async: true, }) // Give time to k3s to fully start await setTimeout(30000) // Get all pods const pods = await sandbox.process.executeCommand( 'sudo /usr/local/bin/kubectl get pod -A' ) console.log(pods.result) ``` ## Default Snapshots Daytona provides pre-built snapshots with fixed resource sizes for creating sandboxes. | **Snapshot** | **vCPU** | **Memory** | **Storage** | **GPU** | | -------------------- | -------- | ---------- | ----------- | ------- | | **`daytona-small`** | 1 | 1GiB | 3GiB | | | **`daytona-medium`** | 2 | 4GiB | 8GiB | | | **`daytona-large`** | 4 | 8GiB | 10GiB | | | **`daytona-gpu`** | 1 | 1GiB | 1GiB | 1 | | **`windows`** | 2 | 8GiB | 30GiB | | Snapshots are based on the `daytonaio/sandbox:` image. ### Python packages (pip) - `anthropic` (v0.76.0) - `beautifulsoup4` (v4.14.3) - `claude-agent-sdk` (v0.1.22) - `openai-agents` (v0.15.1) - `daytona` (v0.134.0) - `django` (v6.0.1) - `flask` (v3.1.2) - `huggingface-hub` (v0.36.0) - `instructor` (v1.14.4) - `keras` (v3.13.0) - `langchain` (v1.2.7) - `llama-index` (v0.14.13) - `matplotlib` (v3.10.8) - `numpy` (v2.4.1) - `ollama` (v0.6.1) - `openai` (v2.33.0) - `opencv-python` (v4.13.0.90) - `pandas` (v2.3.3) - `pillow` (v12.1.0) - `pipx` (v1.8.0) - `pydantic-ai` (v1.47.0) - `python-lsp-server` (v1.14.0) - `requests` (v2.32.5) - `scikit-learn` (v1.8.0) - `scipy` (v1.17.0) - `seaborn` (v0.13.2) - `sqlalchemy` (v2.0.46) - `torch` (v2.10.0) - `transformers` (v4.57.6) - `uv` (v0.9.26) ### Node.js packages (npm) - `@anthropic-ai/claude-code` (v2.1.19) - `@openai/codex` (0.128.0) - `bun` (v1.3.6) - `openclaw` (v2026.2.1) - `opencode-ai` (v1.1.35) - `ts-node` (v10.9.2) - `typescript` (v5.9.3) - `typescript-language-server` (v5.1.3) # 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 Daytona provides an option to create declarative images on-the-fly when creating sandboxes. This is ideal for iterating quickly without creating separate snapshots. Declarative images are cached for 24 hours, and are automatically reused when running the same script. Thus, subsequent runs on the same runner will be almost instantaneous. ```python # Define a declarative image with python packages declarative_image = ( Image.debian_slim("3.12") .pip_install(["requests", "pytest"]) .workdir("/home/daytona") ) # Create a new sandbox with the declarative image and stream the build logs sandbox = daytona.create( CreateSandboxFromImageParams(image=declarative_image), timeout=0, on_snapshot_create_logs=print, ) ``` ```typescript // Define a declarative image with python packages const declarativeImage = Image.debianSlim('3.12') .pipInstall(['requests', 'pytest']) .workdir('/home/daytona') // Create a new sandbox with the declarative image and stream the build logs const sandbox = await daytona.create( { image: declarativeImage, }, { timeout: 0, onSnapshotCreateLogs: console.log, } ) ``` ```ruby # Define a simple declarative image with Python packages declarative_image = Daytona::Image .debian_slim('3.12') .pip_install(['requests', 'pytest']) .workdir('/home/daytona') # Create a new Sandbox with the declarative image and stream the build logs sandbox = daytona.create( Daytona::CreateSandboxFromImageParams.new(image: declarative_image), on_snapshot_create_logs: proc { |chunk| puts chunk } ) ``` ```go // Define a declarative image with python packages version := "3.12" declarativeImage := daytona.DebianSlim(&version). PipInstall([]string{"requests", "pytest"}). Workdir("/home/daytona") // Create a new sandbox with the declarative image and stream the build logs logChan := make(chan string) go func() { for log := range logChan { fmt.Print(log) } }() sandbox, err := client.Create(ctx, types.ImageParams{ Image: declarativeImage, }, options.WithTimeout(0), options.WithLogChannel(logChan)) if err != nil { // handle error } ``` ```java // Define a declarative image with python packages Image declarativeImage = Image.debianSlim("3.12") .pipInstall("requests", "pytest") .workdir("/home/daytona"); // Create a new sandbox with the declarative image and stream the build logs CreateSandboxFromImageParams params = new CreateSandboxFromImageParams(); params.setImage(declarativeImage); Sandbox sandbox = daytona.create(params, 0L, System.out::println); ``` :::note Use the following best practices when working with the declarative builder: - **Layer Optimization**: Group related operations to minimize Docker layers - **Cache Utilization**: Identical build commands and context will be cached and subsequent builds will be almost instant - **Security**: Create non-root users for application workloads - **Resource Efficiency**: Use slim base images when appropriate - **Context Minimization**: Only include necessary files in the build context ::: ## Create pre-built Snapshots Daytona provides an option to [create pre-built snapshots](https://www.daytona.io/docs/snapshots.md#create-snapshots) that can be reused across multiple sandboxes. The snapshot remains visible in the [Daytona Dashboard ↗](https://app.daytona.io/dashboard/snapshots) and is permanently cached, ensuring instant availability without rebuilding. ```python # Create a python data science image snapshot_name = "data-science-snapshot" image = ( Image.debian_slim("3.12") .pip_install(["pandas", "numpy"]) .workdir("/home/daytona") ) # Create the snapshot and stream the build logs daytona.snapshot.create( CreateSnapshotParams( name=snapshot_name, image=image, ), on_logs=print, ) # Create a new sandbox using the pre-built snapshot sandbox = daytona.create( CreateSandboxFromSnapshotParams(snapshot=snapshot_name) ) ``` ```typescript // Create a python data science image const snapshotName = 'data-science-snapshot' const image = Image.debianSlim('3.12') .pipInstall(['pandas', 'numpy']) .workdir('/home/daytona') // Create the snapshot and stream the build logs await daytona.snapshot.create( { name: snapshotName, image, }, { onLogs: console.log, } ) // Create a new sandbox using the pre-built snapshot const sandbox = await daytona.create({ snapshot: snapshotName, }) ``` ```ruby # Create a simple Python data science image snapshot_name = 'data-science-snapshot' image = Daytona::Image .debian_slim('3.12') .pip_install(['pandas', 'numpy']) .workdir('/home/daytona') # Create the Snapshot and stream the build logs daytona.snapshot.create( Daytona::CreateSnapshotParams.new( name: snapshot_name, image: image ), on_logs: proc { |chunk| puts chunk } ) # Create a new Sandbox using the pre-built Snapshot sandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new(snapshot: snapshot_name) ) ``` ```go // Create a python data science image snapshotName := "data-science-snapshot" version := "3.12" image := daytona.DebianSlim(&version). PipInstall([]string{"pandas", "numpy"}). Workdir("/home/daytona") // Create the snapshot and stream the build logs _, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{ Name: snapshotName, Image: image, }) if err != nil { // handle error } for log := range logChan { fmt.Print(log) } // Create a new sandbox using the pre-built snapshot sandbox, err := client.Create(ctx, types.SnapshotParams{ Snapshot: snapshotName, }) if err != nil { // handle error } ``` ```java // Create a python data science image String snapshotName = "data-science-snapshot"; Image image = Image.debianSlim("3.12") .pipInstall("pandas", "numpy") .workdir("/home/daytona"); // Create the snapshot and stream the build logs daytona.snapshot().create(snapshotName, image, System.out::println); // Create a new sandbox using the pre-built snapshot CreateSandboxFromSnapshotParams snapshotParams = new CreateSandboxFromSnapshotParams(); snapshotParams.setSnapshot(snapshotName); Sandbox sandbox = daytona.create(snapshotParams); ``` ## Image configuration Daytona provides an option to define images programmatically using the Daytona SDK. You can specify base images, install packages, add files, set environment variables, and more. For a complete API reference and method signatures, see the [Python](https://www.daytona.io/docs/python-sdk/common/image.md), [TypeScript](https://www.daytona.io/docs/typescript-sdk/image.md), [Ruby](https://www.daytona.io/docs/ruby-sdk/image.md), [Go](https://www.daytona.io/docs/go-sdk/daytona.md#type-DockerImage), and [Java](https://www.daytona.io/docs/java-sdk/image.md) SDK references. ### Base image selection Daytona provides an option to select base images. The following snippets demonstrate how to select and configure base images: ```python # Create an image from a base image = Image.base("python:3.12-slim-bookworm") # Use a Debian slim image with Python 3.12 image = Image.debian_slim("3.12") ``` ```typescript // Create an image from a base const image = Image.base('python:3.12-slim-bookworm') // Use a Debian slim image with Python 3.12 const image = Image.debianSlim('3.12') ``` ```ruby # Create an image from a base image = Daytona::Image.base('python:3.12-slim-bookworm') # Use a Debian slim image with Python 3.12 image = Daytona::Image.debian_slim('3.12') ``` ```go // Create an image from a base image := daytona.Base("python:3.12-slim-bookworm") // Use a Debian slim image with Python 3.12 version := "3.12" image := daytona.DebianSlim(&version) ``` ```java // Create an image from a base Image image = Image.base("python:3.12-slim-bookworm"); // Use a Debian slim image with Python 3.12 image = Image.debianSlim("3.12"); ``` ### Package management Daytona provides an option to install packages and dependencies to your image. The following snippets demonstrate how to install packages and dependencies to your image: ```python # Add pip packages image = Image.debian_slim("3.12").pip_install(["requests", "pandas"]) # Install from requirements.txt image = Image.debian_slim("3.12").pip_install_from_requirements("requirements.txt") # Install from pyproject.toml (with optional dependencies) image = Image.debian_slim("3.12").pip_install_from_pyproject("pyproject.toml", optional_dependencies=["dev"]) ``` ```typescript // Add pip packages const image = Image.debianSlim('3.12').pipInstall(['requests', 'pandas']) // Install from requirements.txt const image = Image.debianSlim('3.12').pipInstallFromRequirements('requirements.txt') // Install from pyproject.toml (with optional dependencies) const image = Image.debianSlim('3.12').pipInstallFromPyproject('pyproject.toml', { optionalDependencies: ['dev'] }) ``` ```ruby # Add pip packages image = Daytona::Image.debian_slim('3.12').pip_install(['requests', 'pandas']) # Install from requirements.txt image = Daytona::Image.debian_slim('3.12').pip_install_from_requirements('requirements.txt') # Install from pyproject.toml (with optional dependencies) image = Daytona::Image.debian_slim('3.12').pip_install_from_pyproject('pyproject.toml', optional_dependencies: ['dev'] ) ``` ```go // Add pip packages version := "3.12" image := daytona.DebianSlim(&version).PipInstall([]string{"requests", "pandas"}) // Install from requirements.txt image := daytona.DebianSlim(&version). AddLocalFile("requirements.txt", "/tmp/requirements.txt"). Run("pip install -r /tmp/requirements.txt") // Install from pyproject.toml (with optional dependencies) image := daytona.DebianSlim(&version). AddLocalFile("pyproject.toml", "/tmp/pyproject.toml"). Run("pip install /tmp[dev]") ``` ```java // Add pip packages Image image = Image.debianSlim("3.12").pipInstall("requests", "pandas"); ``` ### File system operations Daytona provides an option to add files and directories to your image. The following snippets demonstrate how to add files and directories to your image: ```python # Add a local file image = Image.debian_slim("3.12").add_local_file("package.json", "/home/daytona/package.json") # Add a local directory image = Image.debian_slim("3.12").add_local_dir("src", "/home/daytona/src") ``` ```typescript // Add a local file const image = Image.debianSlim('3.12').addLocalFile('package.json', '/home/daytona/package.json') // Add a local directory const image = Image.debianSlim('3.12').addLocalDir('src', '/home/daytona/src') ``` ```ruby # Add a local file image = Daytona::Image.debian_slim('3.12').add_local_file('package.json', '/home/daytona/package.json') # Add a local directory image = Daytona::Image.debian_slim('3.12').add_local_dir('src', '/home/daytona/src') ``` ```go // Add a local file version := "3.12" image := daytona.DebianSlim(&version).AddLocalFile("package.json", "/home/daytona/package.json") // Add a local directory image := daytona.DebianSlim(&version).AddLocalDir("src", "/home/daytona/src") ``` ### Environment configuration Daytona provides an option to configure environment variables and working directories. The following snippets demonstrate how to configure environment variables and working directories: ```python # Set environment variables image = Image.debian_slim("3.12").env({"PROJECT_ROOT": "/home/daytona"}) # Set working directory image = Image.debian_slim("3.12").workdir("/home/daytona") ``` ```typescript // Set environment variables const image = Image.debianSlim('3.12').env({ PROJECT_ROOT: '/home/daytona' }) // Set working directory const image = Image.debianSlim('3.12').workdir('/home/daytona') ``` ```ruby # Set environment variables image = Daytona::Image.debian_slim('3.12').env({ 'PROJECT_ROOT' => '/home/daytona' }) # Set working directory image = Daytona::Image.debian_slim('3.12').workdir('/home/daytona') ``` ```go // Set environment variables version := "3.12" image := daytona.DebianSlim(&version).Env("PROJECT_ROOT", "/home/daytona") // Set working directory image := daytona.DebianSlim(&version).Workdir("/home/daytona") ``` ```java // Set environment variables Image image = Image.debianSlim("3.12") .env(java.util.Map.of("PROJECT_ROOT", "/home/daytona")); // Set working directory image = Image.debianSlim("3.12").workdir("/home/daytona"); ``` ### Commands and entrypoints Daytona provides an option to execute commands during build and configure container startup behavior. The following snippets demonstrate how to execute commands during build and configure container startup behavior: ```python # Run shell commands during build image = Image.debian_slim("3.12").run_commands( 'apt-get update && apt-get install -y git', 'groupadd -r daytona && useradd -r -g daytona -m daytona', 'mkdir -p /home/daytona/workspace' ) # Set entrypoint image = Image.debian_slim("3.12").entrypoint(["/bin/bash"]) # Set default command image = Image.debian_slim("3.12").cmd(["/bin/bash"]) ``` ```typescript // Run shell commands during build const image = Image.debianSlim('3.12').runCommands( 'apt-get update && apt-get install -y git', 'groupadd -r daytona && useradd -r -g daytona -m daytona', 'mkdir -p /home/daytona/workspace' ) // Set entrypoint const image = Image.debianSlim('3.12').entrypoint(['/bin/bash']) // Set default command const image = Image.debianSlim('3.12').cmd(['/bin/bash']) ``` ```ruby # Run shell commands during build image = Daytona::Image.debian_slim('3.12').run_commands( 'apt-get update && apt-get install -y git', 'groupadd -r daytona && useradd -r -g daytona -m daytona', 'mkdir -p /home/daytona/workspace' ) # Set entrypoint image = Daytona::Image.debian_slim('3.12').entrypoint(['/bin/bash']) # Set default command image = Daytona::Image.debian_slim('3.12').cmd(['/bin/bash']) ``` ```go // Run shell commands during build version := "3.12" image := daytona.DebianSlim(&version). Run("apt-get update && apt-get install -y git"). Run("groupadd -r daytona && useradd -r -g daytona -m daytona"). Run("mkdir -p /home/daytona/workspace") // Set entrypoint image := daytona.DebianSlim(&version).Entrypoint([]string{"/bin/bash"}) // Set default command image := daytona.DebianSlim(&version).Cmd([]string{"/bin/bash"}) ``` ```java // Run shell commands during build Image image = Image.debianSlim("3.12").runCommands( "apt-get update && apt-get install -y git", "groupadd -r daytona && useradd -r -g daytona -m daytona", "mkdir -p /home/daytona/workspace" ); // Set entrypoint image = Image.debianSlim("3.12").entrypoint("/bin/bash"); // Set default command image = Image.debianSlim("3.12").cmd("/bin/bash"); ``` ### Dockerfile integration Daytona provides an option to integrate existing Dockerfiles or add custom Dockerfile commands. The following snippets demonstrate how to integrate existing Dockerfiles or add 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"}) ``` ### System package installation Daytona provides an option to install OS-level packages during the image build. Use this pattern when your sandbox needs CLI tools or system libraries that are not available through `pip`. Each string passed to `run_commands` becomes a separate Dockerfile `RUN` instruction, and every `RUN` produces an immutable layer. To keep the image small, chain the package install and the apt cache cleanup together with `&&` inside a single string so the cache is never persisted in any layer. ```python image = Image.debian_slim("3.12").run_commands( "apt-get update " "&& apt-get install -y --no-install-recommends git curl ffmpeg jq " "&& rm -rf /var/lib/apt/lists/*" ) ``` ```typescript const image = Image.debianSlim('3.12').runCommands( 'apt-get update ' + '&& apt-get install -y --no-install-recommends git curl ffmpeg jq ' + '&& rm -rf /var/lib/apt/lists/*', ) ``` ```ruby image = Daytona::Image .debian_slim('3.12') .run_commands( 'apt-get update ' \ '&& apt-get install -y --no-install-recommends git curl ffmpeg jq ' \ '&& rm -rf /var/lib/apt/lists/*' ) ``` ```go version := "3.12" image := daytona.DebianSlim(&version). AptGet([]string{"git", "curl", "ffmpeg", "jq"}) ``` ```java Image image = Image.debianSlim("3.12").runCommands( "apt-get update " + "&& apt-get install -y --no-install-recommends git curl ffmpeg jq " + "&& rm -rf /var/lib/apt/lists/*" ); ``` ### Non-root user setup Daytona provides an option to define a non-root user for application workloads. Run all installation steps as `root` first, then create the user, fix ownership of the working directory, and switch to the new user with the `USER` directive. Subsequent commands and the sandbox runtime then operate without root privileges. Place all installation steps before the `USER` directive. After switching to the non-root user, commands that write to system locations (such as `apt-get install` or `pip install` without `--user`) will fail with permission errors. ```python image = ( Image.debian_slim("3.12") .pip_install(["fastapi", "uvicorn"]) .run_commands( "groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona", "chown -R daytona:daytona /home/daytona", ) .workdir("/home/daytona") .dockerfile_commands(["USER daytona"]) ) ``` ```typescript const image = Image.debianSlim('3.12') .pipInstall(['fastapi', 'uvicorn']) .runCommands( 'groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona', 'chown -R daytona:daytona /home/daytona', ) .workdir('/home/daytona') .dockerfileCommands(['USER daytona']) ``` ```ruby image = Daytona::Image .debian_slim('3.12') .pip_install(['fastapi', 'uvicorn']) .run_commands( 'groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona', 'chown -R daytona:daytona /home/daytona' ) .workdir('/home/daytona') .dockerfile_commands(['USER daytona']) ``` ```go version := "3.12" image := daytona.DebianSlim(&version). PipInstall([]string{"fastapi", "uvicorn"}). Run("groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona"). Run("chown -R daytona:daytona /home/daytona"). Workdir("/home/daytona"). User("daytona") ``` ### Multi-language runtimes Daytona provides an option to combine multiple language runtimes in a single image. The following pattern adds Node.js 20 to a Python base image by installing it from the NodeSource repository. The same approach works for adding Go, Ruby, Java, or any other runtime that distributes a Linux installer. Chain the apt operations, the NodeSource installer, and the cache cleanup into a single `RUN` instruction. If the cache cleanup runs in a separate `RUN`, the apt cache is already persisted in the earlier layers and the final image keeps those bytes. ```python image = ( Image.debian_slim("3.12") .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/*" ) .pip_install(["fastapi", "uvicorn"]) ) ``` ```typescript const image = Image.debianSlim('3.12') .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/*', ) .pipInstall(['fastapi', 'uvicorn']) ``` ```ruby image = Daytona::Image .debian_slim('3.12') .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/*' ) .pip_install(['fastapi', 'uvicorn']) ``` ```go version := "3.12" image := daytona.DebianSlim(&version). 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/*"). PipInstall([]string{"fastapi", "uvicorn"}) ``` ```java Image image = Image.debianSlim("3.12") .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/*" ) .pipInstall("fastapi", "uvicorn"); ``` # 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 Daytona provides methods to create volumes using the [Daytona Dashboard ↗](https://app.daytona.io/dashboard/volumes) or programmatically using the Daytona [Python](https://www.daytona.io/docs/en/python-sdk/sync/volume.md), [TypeScript](https://www.daytona.io/docs/en/typescript-sdk/volume.md), [Ruby](https://www.daytona.io/docs/en/ruby-sdk/volume.md), [Go](https://www.daytona.io/docs/en/go-sdk/daytona.md#type-volumeservice), [Java](https://www.daytona.io/docs/en/java-sdk/volume-service.md) **SDKs**, [CLI](https://www.daytona.io/docs/en/tools/cli.md#daytona-create), or [API](https://www.daytona.io/docs/en/tools/api.md#daytona/tag/sandbox). 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. Navigate to [Daytona Volumes ↗](https://app.daytona.io/dashboard/volumes) 2. Click the **Create Volume** button 3. Enter the volume name ```python from daytona import Daytona daytona = Daytona() volume = daytona.volume.create("my-awesome-volume") ``` ```typescript import { Daytona } from "@daytona/sdk"; const daytona = new Daytona(); const volume = await daytona.volume.create("my-awesome-volume"); ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new volume = daytona.volume.create("my-awesome-volume") ``` ```go package main import ( "context" "fmt" "log" "github.com/daytonaio/daytona/libs/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" }' ``` ## Mount volumes Daytona provides an option to mount a volume to a sandbox. Once a volume is created, it can be mounted to a sandbox by specifying it in the `CreateSandboxFromSnapshotParams` object. 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, for example shared assets or single-tenant workloads. 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` ```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' import path from 'path' 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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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 Daytona provides an option to read from and write to the volume just like any other directory in the sandbox file system. Files written to the volume persist beyond the lifecycle of any individual sandbox. The following snippet demonstrate how to read from and write to a volume: ```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 Daytona provides an option to 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 ' ``` ## List volumes Daytona provides an option to 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 Daytona provides an option to delete a volume. Deleted volumes cannot be recovered. The following snippet demonstrate how to delete a volume: ```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 Daytona provides an option to 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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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 Daytona provides an option to mount more than one volume to a single sandbox by passing multiple 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 Daytona provides an option to isolate per-tenant or per-user data inside a single shared volume by setting a unique `subpath` on each sandbox's volume mount. Each sandbox sees only files under its assigned subpath at `mount_path` and cannot read or write sibling subpaths within the same volume. This is the recommended pattern for multi-tenant workloads because it stays within the [per-organization volume limit](#pricing--limits) instead of creating one volume per tenant. Isolation is enforced at the FUSE mount boundary. Each sandbox sees its assigned subpath as the volume root, so a sandbox mounted at `users/alice` cannot reach `users/bob` through relative paths such as `../bob`. ```python from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount daytona = Daytona() volume = daytona.volume.get("tenants", create=True) mount_dir = "/home/daytona/data" # Tenant A alice_sandbox = daytona.create(CreateSandboxFromSnapshotParams( language="python", volumes=[VolumeMount(volume_id=volume.id, mount_path=mount_dir, subpath="users/alice")], )) alice_sandbox.fs.upload_file(b"alice's data", f"{mount_dir}/notes.txt") # Tenant B sees only its own subpath; alice's notes.txt is invisible bob_sandbox = daytona.create(CreateSandboxFromSnapshotParams( language="python", volumes=[VolumeMount(volume_id=volume.id, mount_path=mount_dir, subpath="users/bob")], )) bob_sandbox.fs.upload_file(b"bob's data", f"{mount_dir}/notes.txt") ``` ```typescript const volume = await daytona.volume.get('tenants', true) const mountDir = '/home/daytona/data' // Tenant A const aliceSandbox = await daytona.create({ language: 'typescript', volumes: [{ volumeId: volume.id, mountPath: mountDir, subpath: 'users/alice' }], }) await aliceSandbox.fs.uploadFile(Buffer.from("alice's data"), `${mountDir}/notes.txt`) // Tenant B sees only its own subpath; alice's notes.txt is invisible const bobSandbox = await daytona.create({ language: 'typescript', volumes: [{ volumeId: volume.id, mountPath: mountDir, subpath: 'users/bob' }], }) await bobSandbox.fs.uploadFile(Buffer.from("bob's data"), `${mountDir}/notes.txt`) ``` ```ruby volume = daytona.volume.get('tenants', create: true) mount_dir = '/home/daytona/data' # Tenant A alice_params = Daytona::CreateSandboxFromSnapshotParams.new( language: Daytona::CodeLanguage::PYTHON, volumes: [DaytonaApiClient::SandboxVolume.new( volume_id: volume.id, mount_path: mount_dir, subpath: 'users/alice' )] ) alice_sandbox = daytona.create(alice_params) alice_sandbox.fs.upload_file("alice's data", "#{mount_dir}/notes.txt") # Tenant B sees only its own subpath; alice's notes.txt is invisible bob_params = Daytona::CreateSandboxFromSnapshotParams.new( language: Daytona::CodeLanguage::PYTHON, volumes: [DaytonaApiClient::SandboxVolume.new( volume_id: volume.id, mount_path: mount_dir, subpath: 'users/bob' )] ) bob_sandbox = daytona.create(bob_params) bob_sandbox.fs.upload_file("bob's data", "#{mount_dir}/notes.txt") ``` ```go volume, err := client.Volume.Get(ctx, "tenants") if err != nil { volume, err = client.Volume.Create(ctx, "tenants") if err != nil { log.Fatal(err) } } mountDir := "/home/daytona/data" // Tenant A aliceSubpath := "users/alice" aliceSandbox, err := client.Create(ctx, types.SnapshotParams{ SandboxBaseParams: types.SandboxBaseParams{ Language: types.CodeLanguagePython, Volumes: []types.VolumeMount{ {VolumeID: volume.ID, MountPath: mountDir, Subpath: &aliceSubpath}, }, }, }) if err != nil { log.Fatal(err) } if err := aliceSandbox.FileSystem.UploadFile(ctx, []byte("alice's data"), mountDir+"/notes.txt"); err != nil { log.Fatal(err) } // Tenant B sees only its own subpath; alice's notes.txt is invisible bobSubpath := "users/bob" bobSandbox, err := client.Create(ctx, types.SnapshotParams{ SandboxBaseParams: types.SandboxBaseParams{ Language: types.CodeLanguagePython, Volumes: []types.VolumeMount{ {VolumeID: volume.ID, MountPath: mountDir, Subpath: &bobSubpath}, }, }, }) if err != nil { log.Fatal(err) } if err := bobSandbox.FileSystem.UploadFile(ctx, []byte("bob's data"), mountDir+"/notes.txt"); err != nil { log.Fatal(err) } ``` The Java SDK and CLI do not currently expose `subpath`. Use the REST API directly when you need subpath mounts from those clients. ```bash # Tenant A curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "volumes": [ { "volumeId": "", "mountPath": "/home/daytona/data", "subpath": "users/alice" } ] }' # Tenant B curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "volumes": [ { "volumeId": "", "mountPath": "/home/daytona/data", "subpath": "users/bob" } ] }' ``` ## Limitations Since volumes are FUSE-based mounts, they can not be used for applications that require block storage access (like database tables). Volumes are generally slower for both read and write operations compared to the local sandbox file system. ## Pricing & Limits Daytona volumes are included at no additional cost. Each organization can create up to 100 volumes, and volume data does not count against your storage quota. You can view your current volume usage in the [Daytona Volumes ↗](https://app.daytona.io/dashboard/volumes). # Mount External Storage Mount object storage (Amazon S3, Cloudflare R2, Google Cloud Storage, Azure Blob) and filesystems like Archil and MesaFS into a Daytona sandbox as a regular directory. The sandbox reads from and writes to the bucket as if it were a local directory, so existing tools, scripts, and agents work without changes. This is useful for bringing in datasets, model weights, or build artifacts that already live in your own cloud account. External storage mounts and [Daytona Volumes](https://www.daytona.io/docs/en/volumes.md) are complementary FUSE-based mechanisms — both expose remote object storage as a regular sandbox directory, both can be shared across sandboxes, and both persist beyond any individual sandbox's lifetime. The main distinction is **where the data physically lives**: Daytona Volumes are hosted on Daytona's own S3-compatible object store, while external mounts connect to a bucket or filesystem hosted on another provider (Amazon S3, Cloudflare R2, GCS, Azure Blob, Archil, MesaFS). External storage is mounted using FUSE. Daytona supports two approaches, and each provider section below shows both — pick whichever fits your workflow: - **Pre-built snapshot** — build a [snapshot](https://www.daytona.io/docs/en/snapshots.md) once with the FUSE tool (`mount-s3`, `gcsfuse`, `blobfuse2`) built-in, then launch every sandbox from that snapshot. Cold starts are fast and predictable. Best for production. - **Runtime install** — launch a default sandbox and `apt-get install` the FUSE tool when the sandbox starts. Adds time to sandbox startup, but you don't manage snapshots. Best for quick experiments. Both approaches end with the same mount command and the same usage — the only difference is when the FUSE tool gets installed. ## Mount an Amazon S3 bucket Mount an S3 bucket using [Mountpoint for Amazon S3 ↗](https://github.com/awslabs/mountpoint-s3) — AWS's official FUSE client, optimized for high throughput on S3. **Credentials** — set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in your local environment. The snippets below pass them into the sandbox via `envVars`, and `mount-s3` reads them from there. ### Pre-built snapshot Build a snapshot with `mount-s3` preinstalled, then launch all S3-enabled sandboxes from that snapshot. This removes per-sandbox package install work, keeps cold starts predictable, and gives you a reusable baseline image for production workloads. #### Build a snapshot Create a reusable snapshot that installs `mount-s3` and its system dependencies. After it finishes, every sandbox launched from `fuse-s3` already has the mount binary available. ```python from daytona import CreateSnapshotParams, Daytona, Image daytona = Daytona() image = ( Image.base("daytonaio/sandbox") .run_commands( "sudo apt-get update " "&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget", 'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' '&& wget -O /tmp/mount-s3.deb ' '"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' "&& sudo apt-get install -y /tmp/mount-s3.deb " "&& rm /tmp/mount-s3.deb", ) ) daytona.snapshot.create( CreateSnapshotParams(name="fuse-s3", image=image), on_logs=print, ) ``` ```typescript import { Daytona, Image } from '@daytona/sdk' const daytona = new Daytona() const image = Image.base('daytonaio/sandbox').runCommands( 'sudo apt-get update ' + '&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget', 'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' + '&& wget -O /tmp/mount-s3.deb ' + '"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' + '&& sudo apt-get install -y /tmp/mount-s3.deb ' + '&& rm /tmp/mount-s3.deb', ) await daytona.snapshot.create( { name: 'fuse-s3', image }, { onLogs: console.log }, ) ``` ```ruby require 'daytona' daytona = Daytona::Daytona.new image = Daytona::Image .base('daytonaio/sandbox') .run_commands( 'sudo apt-get update ' \ '&& sudo apt-get install -y --no-install-recommends libfuse2 ca-certificates wget', 'arch="$(dpkg --print-architecture | sed s/amd64/x86_64/)" ' \ '&& wget -O /tmp/mount-s3.deb ' \ '"https://s3.amazonaws.com/mountpoint-s3-release/latest/${arch}/mount-s3.deb" ' \ '&& sudo apt-get install -y /tmp/mount-s3.deb ' \ '&& rm /tmp/mount-s3.deb' ) daytona.snapshot.create( Daytona::CreateSnapshotParams.new(name: 'fuse-s3', image: image), on_logs: proc { |chunk| print(chunk) } ) ``` ```go import ( "context" "fmt" "log" "github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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/daytonaio/daytona/libs/sdk-go/pkg/daytona" "github.com/daytonaio/daytona/libs/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.CreateSandboxBaseParams; import io.daytona.sdk.model.ExecuteResponse; import java.util.Map; public class App { public static void main(String[] args) { try (Daytona daytona = new Daytona()) { CreateSandboxBaseParams params = new CreateSandboxBaseParams(); 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=