# Daytona Documentation
# https://www.daytona.io/docs
# Generated on: 2026-05-19
# 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-java:0.1.0")
}
```
**Maven**
Add the Daytona SDK dependency to your `pom.xml`:
```xml
io.daytona
sdk-java
0.1.0
```
## 4. Create a Sandbox
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-ghcr)
- [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
```
```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 upgrade the Daytona CLI to the latest version:
```bash
brew upgrade daytonaio/cli/daytona
```
```bash
powershell -Command "irm https://get.daytona.io/windows | iex"
```
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 environment where they can install packages, run servers, compile code, and manage processes.
Sandboxes have **1 vCPU**, **1GB RAM**, and **3GiB disk** by default. [Organizations](https://www.daytona.io/docs/en/organizations.md) get a maximum sandbox resource limit of **4 vCPUs**, **8GB RAM**, and **10GB disk**. For more power, see [resources](#resources) or contact [support@daytona.io](mailto:support@daytona.io).
Sandboxes use [snapshots](https://www.daytona.io/docs/en/snapshots.md) to capture a fully configured environment (base OS, 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. For details on configuring network access, see [network limits](https://www.daytona.io/docs/en/network-limits.md).
A detailed overview of the Daytona platform is available in the [architecture](https://www.daytona.io/docs/en/architecture.md) section.
## Sandbox lifecycle
A sandbox can have several different states. Each state reflects the current 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
To view or update the current state of a sandbox, navigate to the [sandbox details page](#sandbox-details-page) or access the sandbox `state` attribute using the [SDKs](https://www.daytona.io/docs/en/getting-started.md#sdks), [API](https://www.daytona.io/docs/en/tools/api.md#daytona/tag/sandbox/GET/sandbox/{sandboxIdOrName}), or [CLI](https://www.daytona.io/docs/en/tools/cli.md#daytona-info).
The diagram below 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.
## Create Sandboxes
Daytona provides methods to create sandboxes using the [Daytona Dashboard ↗](https://app.daytona.io/dashboard/) or programmatically using the Daytona [Python](https://www.daytona.io/docs/en/python-sdk/sync/sandbox.md), [TypeScript](https://www.daytona.io/docs/en/typescript-sdk/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) **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).
You can specify [programming language runtime](https://www.daytona.io/docs/en/sandboxes.md#multiple-runtime-support), [snapshots](https://www.daytona.io/docs/en/snapshots.md), [resources](https://www.daytona.io/docs/en/sandboxes.md#resources), [regions](https://www.daytona.io/docs/en/regions.md), [environment variables](https://www.daytona.io/docs/en/configuration.md), and [volumes](https://www.daytona.io/docs/en/volumes.md) for each sandbox.
1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click **Create Sandbox**
3. Click **Create** to create a sandbox
Optionally, specify more parameters when creating a sandbox:
- **Name**: enter the name of the sandbox
- **Source**: select the source of the sandbox; [snapshot](https://www.daytona.io/docs/en/snapshots.md) (a pre-configured sandbox template) or image (an OCI-compliant container image: [public](https://www.daytona.io/docs/en/snapshots.md#using-public-images), [local](https://www.daytona.io/docs/en/snapshots.md#using-local-images), [private registries](https://www.daytona.io/docs/en/snapshots.md#using-images-from-private-registries)).
- **Region**: select the region for the sandbox
- **Lifecycle**: define [sandbox lifecycle management](#automated-lifecycle-management) or set as an [ephemeral sandbox](#ephemeral-sandboxes)
- **Environment variables**: set in key-value pairs or import them from a **`.env`** file
- **Labels**: set in key-value pairs to categorize and organize sandboxes
- **Network settings**: [public HTTP preview](https://www.daytona.io/docs/en/preview.md) or [block all network access](https://www.daytona.io/docs/en/network-limits.md)
```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 '{}'
```
### 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`** |
To set custom sandbox resources, use the `Resources` class. 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. Contact [support@daytona.io](mailto:support@daytona.io) to increase limits.
```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
# --memory is in MB; --disk is in GB
daytona create --cpu 2 --memory 4096 --disk 8
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"cpu": 2,
"memory": 4,
"disk": 8
}'
```
### GPU Sandboxes
:::caution[Experimental]
This feature is experimental. To request access, contact [support@daytona.io](mailto:support@daytona.io).
:::
Daytona provides methods to create GPU sandboxes using the [Daytona Dashboard ↗](https://app.daytona.io/dashboard/sandboxes) or programmatically using the Daytona [Python](https://www.daytona.io/docs/en/python-sdk/sync/sandbox.md), [TypeScript](https://www.daytona.io/docs/en/typescript-sdk/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) **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).
Daytona supports NVIDIA GPU devices for snapshot-based sandbox creation. This allows you to run GPU workloads such as model inference, fine-tuning, and CUDA-accelerated compute inside a sandbox created from a [GPU snapshot](https://www.daytona.io/docs/en/snapshots.md#gpu-snapshots). GPU sandboxes must be ephemeral.
1. Create a [GPU Snapshot](https://www.daytona.io/docs/en/snapshots.md#gpu-snapshots)
2. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
3. Click **Create Sandbox**
4. Select your GPU snapshot
5. Click **Create** to create a GPU sandbox
```python
from daytona import Daytona, CreateSandboxFromSnapshotParams
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="my-gpu-snapshot",
ephemeral=True,
)
)
```
```typescript
import { Daytona } from "@daytona/sdk";
const daytona = new Daytona();
const sandbox = await daytona.create({
snapshot: "my-gpu-snapshot",
ephemeral: true,
});
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'my-gpu-snapshot',
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.NewClient()
ctx := context.Background()
params := types.SnapshotParams{
Snapshot: "my-gpu-snapshot",
SandboxBaseParams: types.SandboxBaseParams{
Ephemeral: true,
},
}
_, _ = client.Create(ctx, params)
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Sandbox;
import io.daytona.sdk.model.CreateSandboxFromSnapshotParams;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("my-gpu-snapshot");
params.setAutoDeleteInterval(0);
Sandbox sandbox = daytona.create(params);
}
}
}
```
```bash
daytona create --snapshot my-gpu-snapshot --auto-delete 0
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{
"snapshot": "my-gpu-snapshot",
"autoDeleteInterval": 0
}'
```
### 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) has the same effect as setting `ephemeral` to `True`.
```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);
}
}
}
```
## Start Sandboxes
Daytona provides methods to start sandboxes in [Daytona Dashboard ↗](https://app.daytona.io/dashboard/) or programmatically using the [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/daytona.md#type-sandbox), [Java](https://www.daytona.io/docs/en/java-sdk/sandbox.md) **SDKs**, [CLI](https://www.daytona.io/docs/en/tools/cli.md), and [API](https://www.daytona.io/docs/en/tools/api.md#daytona/).
1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click the start icon (**▶**) next to the sandbox you want to start
```text
Starting sandbox with ID:
```
```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'
```
## List Sandboxes
Daytona provides methods to list sandboxes and view their details in [Daytona Dashboard ↗](https://app.daytona.io/dashboard/) via the [sandbox details page](#sandbox-details-page) or programmatically using the [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/daytona.md), [Java](https://www.daytona.io/docs/en/java-sdk/daytona.md) **SDKs**, [CLI](https://www.daytona.io/docs/en/tools/cli.md), and [API](https://www.daytona.io/docs/en/tools/api.md#daytona).
```python
daytona.list()
```
```typescript
await daytona.list()
```
```ruby
daytona.list
```
```go
result, err := client.List(ctx, nil, nil, nil)
```
```java
daytona.list();
```
```bash
daytona list [flags]
```
```bash
curl 'https://app.daytona.io/api/sandbox' \
--header 'Authorization: Bearer YOUR_API_KEY'
```
##### Sandbox details page
[Daytona Dashboard ↗](https://app.daytona.io/dashboard/) provides a sandbox details page to view detailed information about a sandbox and interact with it directly.
1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click on a sandbox you want to view the details of
3. Click **View** to open the sandbox details page
The sandbox details page provides a summary of the sandbox information and actions to perform on the sandbox:
- **Name**: the name of the sandbox
- **UUID**: the unique identifier of the sandbox
- **State**: the sandbox state with a visual indicator
- **Actions**: [start](#start-sandboxes), [stop](#stop-sandboxes), [recover](#recover-sandboxes), [archive](#archive-sandboxes), [delete](#delete-sandboxes), refresh, [SSH access](https://www.daytona.io/docs/en/ssh-access.md), [screen recordings](https://www.daytona.io/docs/en/computer-use.md#screen-recording)
- [**Region**](https://www.daytona.io/docs/en/regions.md): the target region where the sandbox is running
- [**Snapshot**](https://www.daytona.io/docs/en/snapshots.md): the snapshot used to create the sandbox
- [**Resources**](#resources): allocated sandbox CPU, memory, and disk
- [**Lifecycle**](#sandbox-lifecycle): [auto-stop](#auto-stop-interval), [auto-archive](#auto-archive-interval), and [auto-delete](#auto-delete-interval) intervals
- **Labels**: key-value pairs assigned to the sandbox
- **Timestamps**: when the sandbox was created and when the last event occurred
- [**Web terminal**](https://www.daytona.io/docs/en/web-terminal.md): an embedded web terminal session directly in the browser
- **Filesystem**: sandbox filesystem tree for viewing and managing files and directories: create, upload, download, copy, refresh, collapse, search, and delete capabilities
- [**VNC**](https://www.daytona.io/docs/en/vnc-access.md): a graphical desktop session for sandboxes that have a desktop environment
- [**Logs**](https://www.daytona.io/docs/en/observability/otel-collection.md): a detailed record of user and system activity for the sandbox
- **Metrics**: sandbox metrics data displayed as charts
- **Traces**: distributed traces and spans collected from the sandbox
- **Spending**: usage and cost over time
## Stop Sandboxes
Daytona provides methods to stop sandboxes in [Daytona Dashboard ↗](https://app.daytona.io/dashboard/) or programmatically using the [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/daytona.md), [Java](https://www.daytona.io/docs/en/java-sdk/daytona.md) **SDKs**, [CLI](https://www.daytona.io/docs/en/tools/cli.md), and [API](https://www.daytona.io/docs/en/tools/api.md#daytona).
Stopped sandboxes maintain filesystem persistence while their memory state is cleared. They incur only disk usage costs and can be started again when needed. 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
```text
Stopping sandbox with ID:
```
```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
:::caution[Experimental]
This feature is experimental. To request access, contact [support@daytona.io](mailto:support@daytona.io).
:::
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 in [Daytona Dashboard ↗](https://app.daytona.io/dashboard/) or programmatically using the [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/daytona.md) **SDKs**, [CLI](https://www.daytona.io/docs/en/tools/cli.md), and [API](https://www.daytona.io/docs/en/tools/api.md#daytona).
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 in [Daytona Dashboard ↗](https://app.daytona.io/dashboard/) or programmatically using the [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/daytona.md) **SDKs**, and [API](https://www.daytona.io/docs/en/tools/api.md#daytona).
```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'
```
##### Recover from error state
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 using [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/daytona.md) **SDKs**, and [API](https://www.daytona.io/docs/en/tools/api.md#daytona). 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
}'
```
## Fork Sandboxes
:::caution[Experimental]
This feature is experimental. To request access, contact [support@daytona.io](mailto:support@daytona.io).
:::
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"
}'
```
##### View Forks
Daytona provides methods to view forks. You can 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 forked sandbox
3. Select **View Forks**
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.
## Create Snapshot from Sandbox
:::caution[Experimental]
This feature is experimental. To request access, contact [support@daytona.io](mailto:support@daytona.io).
:::
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.
```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"
}'
```
## Delete Sandboxes
Daytona provides methods to delete sandboxes in [Daytona Dashboard ↗](https://app.daytona.io/dashboard/) or programmatically using the [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/daytona.md), [Java](https://www.daytona.io/docs/en/java-sdk/daytona.md) **SDKs**, [CLI](https://www.daytona.io/docs/en/tools/cli.md), and [API](https://www.daytona.io/docs/en/tools/api.md#daytona).
1. Navigate to [Daytona Sandboxes ↗](https://app.daytona.io/dashboard/sandboxes)
2. Click the **Delete** button next to the sandbox you want to delete.
```text
Deleting sandbox with ID:
```
```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'
```
## Automated lifecycle management
Daytona sandboxes can be automatically stopped, archived, and deleted based on user-defined intervals.
### Auto-stop interval
The auto-stop interval parameter sets the amount of time after which a running sandbox will be automatically stopped.
The auto-stop interval 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 using the [Python SDK](https://www.daytona.io/docs/en/python-sdk.md), [TypeScript SDK](https://www.daytona.io/docs/en/typescript-sdk.md), [Ruby SDK](https://www.daytona.io/docs/en/ruby-sdk.md), and [Java SDK](https://www.daytona.io/docs/en/java-sdk/sandbox.md).
The auto-archive interval parameter 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 using the [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/daytona.md), [Java](https://www.daytona.io/docs/en/java-sdk/daytona.md) **SDKs**, and [API](https://www.daytona.io/docs/en/tools/api.md#daytona).
The auto-delete interval parameter 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 using the [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/daytona.md), and [Java](https://www.daytona.io/docs/en/java-sdk/daytona.md) **SDKs**.
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 sandbox templates created from [Docker](https://www.docker.com/) or [OCI](https://opencontainers.org/) compatible images. Sandboxes can use a [default snapshot](#default-snapshots) or custom snapshots to provide a consistent and reproducible sandbox environments for your dependencies, settings, and resources.
Daytona supports running [Docker](#run-docker-in-a-sandbox) and [Kubernetes](#run-kubernetes-in-a-sandbox) workloads inside sandboxes using snapshots.
## Snapshot lifecycle
A snapshot can have several different states. Each state reflects the current status of your snapshot.
- **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.
:::
## Create Snapshots
Daytona provides methods to create snapshots using the [Daytona Dashboard ↗](https://app.daytona.io/dashboard/snapshots) or programmatically using the Daytona [Python](https://www.daytona.io/docs/en/python-sdk/sync/snapshot.md), [TypeScript](https://www.daytona.io/docs/en/typescript-sdk/snapshot.md), [Ruby](https://www.daytona.io/docs/en/ruby-sdk/snapshot.md), [Go](https://www.daytona.io/docs/en/go-sdk/daytona.md#SnapshotService), [Java](https://www.daytona.io/docs/en/java-sdk/snapshot.md) **SDKs**, [CLI](https://www.daytona.io/docs/en/tools/cli.md#daytona-snapshot), or [API](https://www.daytona.io/docs/en/tools/api.md#daytona/tag/snapshots).
Snapshots can be created using:
- [public images](#using-public-images)
- [local images](#using-local-images)
- [images from private registries](#using-images-from-private-registries)
- [the declarative builder](#using-the-declarative-builder)
- [GPU snapshots](#gpu-snapshots) (for [GPU sandboxes](https://www.daytona.io/docs/en/sandboxes.md#gpu-sandboxes))
1. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click the **Create Snapshot** button
3. Enter the **snapshot name**, **image** (tag or digest), **entrypoint**, and **resources**
- **Snapshot name**: Identifier used to reference the snapshot in the SDK or CLI.
- **Image**: Base image for the snapshot. Must include either a tag or a digest (e.g., **`ubuntu:22.04`**). The **`latest`** tag is not allowed. Since images tagged `latest` get frequent updates, only specific tags are supported. Same applies to tags such as `lts` or `stable`, and we recommend avoiding those when defining an image to prevent unexpected behavior.
- **Entrypoint** (optional): The entrypoint command for the snapshot. Ensure that the entrypoint is a long-running command. If not provided, or if the snapshot does not have an entrypoint, `sleep infinity` will be used as the default.
- [**Resources**](https://www.daytona.io/docs/en/sandboxes.md#resources) (optional): The resources you want the underlying Sandboxes to have. By default, Daytona Sandboxes use **1 vCPU**, **1GiB memory**, and **3GiB storage**.
- **GPU** (optional): Enable the **GPU** option to create a GPU snapshot for [GPU sandboxes](https://www.daytona.io/docs/en/sandboxes.md#gpu-sandboxes).
```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
}'
```
### Using 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** (tag or digest) of any publicly accessible image or container registry
Once the snapshot is pulled, validated, and has an `Active` state, it is ready to be used.
```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"
}'
```
### Using 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
```
:::tip
Use the flags `--cpu`, `--memory` and `--disk` to specify the [resources](https://www.daytona.io/docs/en/sandboxes.md#resources) you want the underlying sandboxes to have. Example:
```bash
daytona snapshot push custom-alpine:3.21 --name alpine-minimal --cpu 2 --memory 4 --disk 8
```
:::
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
```
```text
Building image from /Users/user/docs/Dockerfile
Step 1/5 : FROM alpine:latest
...
⡿ Waiting for the Snapshot to be validated ...
...
✓ Use 'harbor-transient.internal.daytona.app/daytona/trying-daytona:0.0.1' to create a new sandbox using this Snapshot
```
### Using 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.
The **Add Registry** form has a tab per provider — Docker Hub, Google, GitHub, Amazon ECR, and Generic. Select the tab that matches your registry; the form will pre-fill or hide fields whose value is fixed for that provider (so you only fill in what's actually account-specific). The provider-specific sections below list exactly what to enter, and what gets auto-filled behind the scenes.
1. Navigate to [Daytona Registries ↗](https://app.daytona.io/dashboard/registries)
2. Click **Add Registry** and pick the tab for your provider
3. Fill in the visible fields (see the section for your provider below)
4. After the registry is created, 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`)
Optionally, set the **`CreateSandboxFromSnapshotParams`** field to use the custom snapshot.
#### Docker Hub
Daytona supports creating snapshots from Docker Hub images.
1. On [Daytona Registries ↗](https://app.daytona.io/dashboard/registries), click **Add Registry** and select the **Docker Hub** tab.
2. Fill in:
- **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 is auto-filled with `docker.io` and not shown in the form.
3. 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. On [Daytona Registries ↗](https://app.daytona.io/dashboard/registries), click **Add Registry** and select the **Google** tab.
2. Fill in:
- **Registry URL**: the base URL for your region (e.g. `https://us-central1-docker.pkg.dev`)
- **Service Account JSON Key**: paste the full contents of your service account key JSON file
- **Google Cloud Project ID**: your GCP project ID
Username is auto-filled with `_json_key` (required by Google for service-account auth) and not shown in the form.
3. Create the snapshot using the full image reference, e.g. `us-central1-docker.pkg.dev///:`.
#### GitHub Container Registry (GHCR)
Daytona supports creating snapshots from images from GitHub Container Registry.
1. On [Daytona Registries ↗](https://app.daytona.io/dashboard/registries), click **Add Registry** and select the **GitHub** tab.
2. Fill in:
- **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 is 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 (ECR)
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--*"
}
```
### Using the 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).
### GPU Snapshots
:::caution[Experimental]
This feature is experimental. To request access, contact [support@daytona.io](mailto:support@daytona.io).
:::
Daytona provides methods to create GPU snapshots using the [Daytona Dashboard ↗](https://app.daytona.io/dashboard/snapshots) or programmatically using the Daytona [Python](https://www.daytona.io/docs/en/python-sdk/sync/snapshot.md), [TypeScript](https://www.daytona.io/docs/en/typescript-sdk/snapshot.md), [Ruby](https://www.daytona.io/docs/en/ruby-sdk/snapshot.md), [Go](https://www.daytona.io/docs/en/go-sdk/daytona.md#SnapshotService), [Java](https://www.daytona.io/docs/en/java-sdk/snapshot.md) **SDKs**, or [API](https://www.daytona.io/docs/en/tools/api.md#daytona/tag/snapshots).
GPU snapshots fit into the same snapshot creation flow as other snapshots, but with the additional option to enable the GPU. Create a GPU snapshot first, then use it as the source when creating a [GPU sandbox](https://www.daytona.io/docs/en/sandboxes.md#gpu-sandboxes).
1. Navigate to [Daytona Snapshots ↗](https://app.daytona.io/dashboard/snapshots)
2. Click the **Create Snapshot** button
3. Configure snapshot details and enable the GPU option
To create a GPU-enabled snapshot programmatically, set GPU resources for the snapshot. The `gpu` value defines how many GPU units each sandbox created from this snapshot will request. If `gpu` is not set, it defaults to `0`, which creates a non-GPU snapshot.
```python
from daytona import Daytona, CreateSnapshotParams, Resources
daytona = Daytona()
snapshot = daytona.snapshot.create(
CreateSnapshotParams(
name="my-gpu-snapshot",
image="python:3.12",
resources=Resources(gpu=1),
),
)
```
```typescript
import { Daytona } from "@daytona/sdk";
const daytona = new Daytona();
const snapshot = await daytona.snapshot.create({
name: "my-gpu-snapshot",
image: "python:3.12",
resources: { gpu: 1 },
});
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
snapshot = daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(
name: 'my-gpu-snapshot',
image: 'python:3.12',
resources: Daytona::Resources.new(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.NewClient()
ctx := context.Background()
snapshot, logCh, _ := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "my-gpu-snapshot",
Image: "python:3.12",
Resources: &types.Resources{
GPU: 1,
},
})
for range logCh {
}
_ = snapshot
}
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.Image;
import io.daytona.sdk.model.Resources;
import io.daytona.sdk.model.Snapshot;
final class CreateGpuSnapshot {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Resources resources = new Resources();
resources.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",
"gpu": 1
}'
```
##### GPU Snapshot requirements
Building a custom image for a GPU snapshot requires Ubuntu 24.04 and compatible NVIDIA userspace packages. Include the following in your snapshot Dockerfile:
```dockerfile
ARG NVIDIA_DRIVER_VERSION=580.126.20
ARG UBUNTU_PKG_SUFFIX=0ubuntu0.24.04.2
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -qq \
&& apt-get install -y -qq --no-install-recommends \
nvidia-utils-580-server=${NVIDIA_DRIVER_VERSION}-${UBUNTU_PKG_SUFFIX} \
libnvidia-compute-580-server=${NVIDIA_DRIVER_VERSION}-${UBUNTU_PKG_SUFFIX} \
python3 \
ca-certificates \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY verify-cuda.sh /verify-cuda.sh
RUN chmod +x /verify-cuda.sh
LABEL nvidia.driver.version="${NVIDIA_DRIVER_VERSION}"
```
### 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 sandbox 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'
```
## 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
When a sandbox is created with no snapshot specified, Daytona uses a default snapshot that includes `python`, `node`, their language servers, and several common pip packages. Daytona provides three default snapshot sizes:
| **Snapshot** | **vCPU** | **Memory** | **Storage** |
| -------------------- | -------- | ---------- | ----------- |
| **`daytona-small`** | 1 | 1GiB | 3GiB |
| **`daytona-medium`** | 2 | 4GiB | 8GiB |
| **`daytona-large`** | 4 | 8GiB | 10GiB |
All default snapshots are based on the `daytonaio/sandbox:` image. For more information, see the [Dockerfile](https://github.com/daytonaio/daytona/blob/main/images/sandbox/Dockerfile).
### 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)
- `pydantic-ai` (v1.47.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)
### 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_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 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, 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 Google Cloud Storage bucket
Mount a GCS bucket using [gcsfuse ↗](https://github.com/GoogleCloudPlatform/gcsfuse) — Google's official FUSE client.
**Credentials** — `gcsfuse` reads a service account JSON key file. The snippets below read the key from a local path on your host and upload it into the sandbox via `sandbox.fs`.
:::note
Daytona's base image is Debian Trixie, but Google has not published a Trixie-specific gcsfuse repository yet. The `gcsfuse-bookworm` repo packages run cleanly on Trixie, so we use them in the snippets below.
:::
### Pre-built snapshot
Build a snapshot with `gcsfuse` preinstalled, then launch all GCS-enabled sandboxes from that snapshot. This avoids repeating apt repository setup and package installation for every sandbox, which makes startup behavior more consistent.
#### Build a snapshot
Create a reusable snapshot that installs `gcsfuse` plus its apt repository configuration. After this step, GCS-enabled sandboxes can mount immediately without repeating package setup.
```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 ca-certificates curl gnupg",
"sudo mkdir -p /etc/apt/keyrings "
"&& curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg "
"| sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg",
'echo "deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] '
'https://packages.cloud.google.com/apt gcsfuse-bookworm main" '
"| sudo tee /etc/apt/sources.list.d/gcsfuse.list",
"sudo apt-get update && sudo apt-get install -y gcsfuse",
)
)
daytona.snapshot.create(
CreateSnapshotParams(name="fuse-gcs", 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 ca-certificates curl gnupg',
'sudo mkdir -p /etc/apt/keyrings ' +
'&& curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg ' +
'| sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg',
'echo "deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] ' +
'https://packages.cloud.google.com/apt gcsfuse-bookworm main" ' +
'| sudo tee /etc/apt/sources.list.d/gcsfuse.list',
'sudo apt-get update && sudo apt-get install -y gcsfuse',
)
await daytona.snapshot.create(
{ name: 'fuse-gcs', 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 ca-certificates curl gnupg',
'sudo mkdir -p /etc/apt/keyrings ' \
'&& curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg ' \
'| sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg',
'echo "deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] ' \
'https://packages.cloud.google.com/apt gcsfuse-bookworm main" ' \
'| sudo tee /etc/apt/sources.list.d/gcsfuse.list',
'sudo apt-get update && sudo apt-get install -y gcsfuse'
)
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(name: 'fuse-gcs', 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 ca-certificates curl gnupg").
Run("sudo mkdir -p /etc/apt/keyrings && " +
"curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | " +
"sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg").
Run(`echo "deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] ` +
`https://packages.cloud.google.com/apt gcsfuse-bookworm main" | ` +
`sudo tee /etc/apt/sources.list.d/gcsfuse.list`).
Run("sudo apt-get update && sudo apt-get install -y gcsfuse")
_, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "fuse-gcs",
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 ca-certificates curl gnupg",
"sudo mkdir -p /etc/apt/keyrings "
+ "&& curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg "
+ "| sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg",
"echo \"deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] "
+ "https://packages.cloud.google.com/apt gcsfuse-bookworm main\" "
+ "| sudo tee /etc/apt/sources.list.d/gcsfuse.list",
"sudo apt-get update && sudo apt-get install -y gcsfuse"
);
daytona.snapshot().create("fuse-gcs", image, System.out::println);
}
}
}
```
#### Launch and mount
`gcsfuse` authenticates to GCS with a service account JSON key. Upload it into the sandbox via `sandbox.fs` and point `gcsfuse` at it with `--key-file`.
```python
import os
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
# GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
service_account_key = os.environ["GCS_SERVICE_ACCOUNT_KEY"].encode()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="fuse-gcs"))
mount_path = "/home/daytona/gcs"
key_path = "/home/daytona/.gcs-key.json"
# Upload the key file into the sandbox
sandbox.fs.upload_file(service_account_key, key_path)
sandbox.process.exec(f"chmod 600 {key_path}")
# Mount the bucket
sandbox.process.exec(f"mkdir -p {mount_path}")
sandbox.process.exec(f"gcsfuse --key-file={key_path} my-gcs-bucket {mount_path}")
# Use the mount
response = sandbox.process.exec(f"ls {mount_path}")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
// GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
const serviceAccountKey = Buffer.from(process.env.GCS_SERVICE_ACCOUNT_KEY!)
const sandbox = await daytona.create({ snapshot: 'fuse-gcs' })
const mountPath = '/home/daytona/gcs'
const keyPath = '/home/daytona/.gcs-key.json'
// Upload the key file into the sandbox
await sandbox.fs.uploadFile(serviceAccountKey, keyPath)
await sandbox.process.executeCommand(`chmod 600 ${keyPath}`)
// Mount the bucket
await sandbox.process.executeCommand(`mkdir -p ${mountPath}`)
await sandbox.process.executeCommand(`gcsfuse --key-file=${keyPath} my-gcs-bucket ${mountPath}`)
// Use the mount
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
# GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
service_account_key = ENV.fetch('GCS_SERVICE_ACCOUNT_KEY')
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(snapshot: 'fuse-gcs')
)
mount_path = '/home/daytona/gcs'
key_path = '/home/daytona/.gcs-key.json'
# Upload the key file into the sandbox
sandbox.fs.upload_file(service_account_key, key_path)
sandbox.process.exec(command: "chmod 600 #{key_path}")
# Mount the bucket
sandbox.process.exec(command: "mkdir -p #{mount_path}")
sandbox.process.exec(command: "gcsfuse --key-file=#{key_path} my-gcs-bucket #{mount_path}")
# Use the mount
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)
}
// GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
serviceAccountKey := []byte(os.Getenv("GCS_SERVICE_ACCOUNT_KEY"))
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: "fuse-gcs",
})
if err != nil {
log.Fatal(err)
}
mountPath := "/home/daytona/gcs"
keyPath := "/home/daytona/.gcs-key.json"
// Upload the key file into the sandbox
if err := sandbox.FileSystem.UploadFile(ctx, serviceAccountKey, keyPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "chmod 600 "+keyPath); err != nil {
log.Fatal(err)
}
// Mount the bucket
if _, err := sandbox.Process.ExecuteCommand(ctx, "mkdir -p "+mountPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
"gcsfuse --key-file="+keyPath+" my-gcs-bucket "+mountPath); err != nil {
log.Fatal(err)
}
// Use the mount
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.nio.charset.StandardCharsets;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
// GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
byte[] serviceAccountKey = System.getenv("GCS_SERVICE_ACCOUNT_KEY")
.getBytes(StandardCharsets.UTF_8);
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("fuse-gcs");
Sandbox sandbox = daytona.create(params);
String mountPath = "/home/daytona/gcs";
String keyPath = "/home/daytona/.gcs-key.json";
// Upload the key file into the sandbox
sandbox.fs.uploadFile(serviceAccountKey, keyPath);
sandbox.getProcess().executeCommand("chmod 600 " + keyPath);
// Mount the bucket
sandbox.getProcess().executeCommand("mkdir -p " + mountPath);
sandbox.getProcess().executeCommand(
"gcsfuse --key-file=" + keyPath + " my-gcs-bucket " + mountPath);
// Use the mount
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
### Runtime install
Start from a default sandbox and install `gcsfuse` when the sandbox starts, then upload the service account key and mount the bucket. This is the fastest way to iterate on setup, but every sandbox repeats install and key staging steps.
```python
import os
from daytona import CreateSandboxBaseParams, Daytona
daytona = Daytona()
# GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
service_account_key = os.environ["GCS_SERVICE_ACCOUNT_KEY"].encode()
sandbox = daytona.create(CreateSandboxBaseParams())
# Install gcsfuse from the bookworm repo (works on Trixie)
sandbox.process.exec(
"sudo apt-get update "
"&& sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg"
)
sandbox.process.exec(
"sudo mkdir -p /etc/apt/keyrings "
"&& curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg "
"| sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg"
)
sandbox.process.exec(
'echo "deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] '
'https://packages.cloud.google.com/apt gcsfuse-bookworm main" '
"| sudo tee /etc/apt/sources.list.d/gcsfuse.list "
"&& sudo apt-get update && sudo apt-get install -y gcsfuse"
)
# Upload the key and mount
mount_path = "/home/daytona/gcs"
key_path = "/home/daytona/.gcs-key.json"
sandbox.fs.upload_file(service_account_key, key_path)
sandbox.process.exec(f"chmod 600 {key_path}")
sandbox.process.exec(f"mkdir -p {mount_path}")
sandbox.process.exec(f"gcsfuse --key-file={key_path} my-gcs-bucket {mount_path}")
response = sandbox.process.exec(f"ls {mount_path}")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
// GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
const serviceAccountKey = Buffer.from(process.env.GCS_SERVICE_ACCOUNT_KEY!)
const sandbox = await daytona.create()
// Install gcsfuse from the bookworm repo (works on Trixie)
await sandbox.process.executeCommand(
'sudo apt-get update ' +
'&& sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg',
)
await sandbox.process.executeCommand(
'sudo mkdir -p /etc/apt/keyrings ' +
'&& curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg ' +
'| sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg',
)
await sandbox.process.executeCommand(
'echo "deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] ' +
'https://packages.cloud.google.com/apt gcsfuse-bookworm main" ' +
'| sudo tee /etc/apt/sources.list.d/gcsfuse.list ' +
'&& sudo apt-get update && sudo apt-get install -y gcsfuse',
)
// Upload the key and mount
const mountPath = '/home/daytona/gcs'
const keyPath = '/home/daytona/.gcs-key.json'
await sandbox.fs.uploadFile(serviceAccountKey, keyPath)
await sandbox.process.executeCommand(`chmod 600 ${keyPath}`)
await sandbox.process.executeCommand(`mkdir -p ${mountPath}`)
await sandbox.process.executeCommand(`gcsfuse --key-file=${keyPath} my-gcs-bucket ${mountPath}`)
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
# GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
service_account_key = ENV.fetch('GCS_SERVICE_ACCOUNT_KEY')
sandbox = daytona.create(Daytona::CreateSandboxBaseParams.new)
# Install gcsfuse from the bookworm repo (works on Trixie)
sandbox.process.exec(
command: 'sudo apt-get update ' \
'&& sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg'
)
sandbox.process.exec(
command: 'sudo mkdir -p /etc/apt/keyrings ' \
'&& curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg ' \
'| sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg'
)
sandbox.process.exec(
command: 'echo "deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] ' \
'https://packages.cloud.google.com/apt gcsfuse-bookworm main" ' \
'| sudo tee /etc/apt/sources.list.d/gcsfuse.list ' \
'&& sudo apt-get update && sudo apt-get install -y gcsfuse'
)
# Upload the key and mount
mount_path = '/home/daytona/gcs'
key_path = '/home/daytona/.gcs-key.json'
sandbox.fs.upload_file(service_account_key, key_path)
sandbox.process.exec(command: "chmod 600 #{key_path}")
sandbox.process.exec(command: "mkdir -p #{mount_path}")
sandbox.process.exec(command: "gcsfuse --key-file=#{key_path} my-gcs-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)
}
// GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
serviceAccountKey := []byte(os.Getenv("GCS_SERVICE_ACCOUNT_KEY"))
sandbox, err := client.Create(ctx, types.SnapshotParams{})
if err != nil {
log.Fatal(err)
}
// Install gcsfuse from the bookworm repo (works on Trixie)
if _, err := sandbox.Process.ExecuteCommand(ctx,
"sudo apt-get update && sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
"sudo mkdir -p /etc/apt/keyrings && "+
"curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | "+
"sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
`echo "deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] `+
`https://packages.cloud.google.com/apt gcsfuse-bookworm main" | `+
`sudo tee /etc/apt/sources.list.d/gcsfuse.list && `+
`sudo apt-get update && sudo apt-get install -y gcsfuse`); err != nil {
log.Fatal(err)
}
// Upload the key and mount
mountPath := "/home/daytona/gcs"
keyPath := "/home/daytona/.gcs-key.json"
if err := sandbox.FileSystem.UploadFile(ctx, serviceAccountKey, keyPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "chmod 600 "+keyPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "mkdir -p "+mountPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
"gcsfuse --key-file="+keyPath+" my-gcs-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.nio.charset.StandardCharsets;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
// GCS_SERVICE_ACCOUNT_KEY holds the full service account JSON as a string
byte[] serviceAccountKey = System.getenv("GCS_SERVICE_ACCOUNT_KEY")
.getBytes(StandardCharsets.UTF_8);
Sandbox sandbox = daytona.create(new CreateSandboxFromSnapshotParams());
// Install gcsfuse from the bookworm repo (works on Trixie)
sandbox.getProcess().executeCommand(
"sudo apt-get update "
+ "&& sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg");
sandbox.getProcess().executeCommand(
"sudo mkdir -p /etc/apt/keyrings "
+ "&& curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg "
+ "| sudo gpg --dearmor -o /etc/apt/keyrings/gcsfuse.gpg");
sandbox.getProcess().executeCommand(
"echo \"deb [signed-by=/etc/apt/keyrings/gcsfuse.gpg] "
+ "https://packages.cloud.google.com/apt gcsfuse-bookworm main\" "
+ "| sudo tee /etc/apt/sources.list.d/gcsfuse.list "
+ "&& sudo apt-get update && sudo apt-get install -y gcsfuse");
// Upload the key and mount
String mountPath = "/home/daytona/gcs";
String keyPath = "/home/daytona/.gcs-key.json";
sandbox.fs.uploadFile(serviceAccountKey, keyPath);
sandbox.getProcess().executeCommand("chmod 600 " + keyPath);
sandbox.getProcess().executeCommand("mkdir -p " + mountPath);
sandbox.getProcess().executeCommand(
"gcsfuse --key-file=" + keyPath + " my-gcs-bucket " + mountPath);
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
## Mount an Azure Blob container
Mount an Azure Blob container using [blobfuse2 ↗](https://github.com/Azure/azure-storage-fuse) — Microsoft's official FUSE client.
**Credentials** — set `AZURE_STORAGE_ACCOUNT`, `AZURE_STORAGE_CONTAINER`, and `AZURE_STORAGE_ACCOUNT_KEY` in your local environment. The snippets below pass them into the sandbox via `envVars`, and `blobfuse2` reads them from its YAML config.
:::caution
Daytona's base image is Debian Trixie. Microsoft's `blobfuse2` Bookworm binary links against `libfuse3.so.3`, but Trixie's `fuse3` package bumped the SONAME to `.4`, so `libfuse3.so.3` is missing on disk. The snapshot recipe below creates a compatibility symlink. Without it, `blobfuse2` fails with `libfuse3.so.3: cannot open shared object file`.
:::
### Pre-built snapshot
Build a snapshot with `blobfuse2` and required FUSE compatibility setup preinstalled, then launch all Azure-enabled sandboxes from that snapshot. This is the recommended path for stable environments because dependency and compatibility work runs once during snapshot creation.
#### Build a snapshot
Create a reusable snapshot that installs `blobfuse2`, configures required FUSE dependencies, and applies the Trixie compatibility steps. This ensures Azure mounts work out of the box in sandboxes launched from `fuse-azure`.
```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 ca-certificates curl gnupg wget",
# Microsoft's apt repo (use bookworm packages on Trixie)
"wget -qO- https://packages.microsoft.com/keys/microsoft.asc "
"| sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg",
'echo "deb [arch=$(dpkg --print-architecture) '
'signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] '
'https://packages.microsoft.com/debian/12/prod bookworm main" '
"| sudo tee /etc/apt/sources.list.d/microsoft-prod.list",
"sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3",
# libfuse3.so.3 compat symlink for Trixie (see :::caution above)
'src=$(find /usr/lib /lib -name "libfuse3.so.3.*" -type f 2>/dev/null '
"| sort -V | tail -1) "
'&& sudo ln -sfn "$src" "$(dirname "$src")/libfuse3.so.3" '
"&& sudo ldconfig",
"sudo touch /etc/fuse.conf "
'&& grep -qxF "user_allow_other" /etc/fuse.conf '
'|| echo "user_allow_other" | sudo tee -a /etc/fuse.conf',
)
)
daytona.snapshot.create(
CreateSnapshotParams(name="fuse-azure", 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 ca-certificates curl gnupg wget',
// Microsoft's apt repo (use bookworm packages on Trixie)
'wget -qO- https://packages.microsoft.com/keys/microsoft.asc ' +
'| sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg',
'echo "deb [arch=$(dpkg --print-architecture) ' +
'signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] ' +
'https://packages.microsoft.com/debian/12/prod bookworm main" ' +
'| sudo tee /etc/apt/sources.list.d/microsoft-prod.list',
'sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3',
// libfuse3.so.3 compat symlink for Trixie (see :::caution above)
'src=$(find /usr/lib /lib -name "libfuse3.so.3.*" -type f 2>/dev/null ' +
'| sort -V | tail -1) ' +
'&& sudo ln -sfn "$src" "$(dirname "$src")/libfuse3.so.3" ' +
'&& sudo ldconfig',
'sudo touch /etc/fuse.conf ' +
'&& grep -qxF "user_allow_other" /etc/fuse.conf ' +
'|| echo "user_allow_other" | sudo tee -a /etc/fuse.conf',
)
await daytona.snapshot.create(
{ name: 'fuse-azure', 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 ca-certificates curl gnupg wget',
# Microsoft's apt repo (use bookworm packages on Trixie)
'wget -qO- https://packages.microsoft.com/keys/microsoft.asc ' \
'| sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg',
'echo "deb [arch=$(dpkg --print-architecture) ' \
'signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] ' \
'https://packages.microsoft.com/debian/12/prod bookworm main" ' \
'| sudo tee /etc/apt/sources.list.d/microsoft-prod.list',
'sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3',
# libfuse3.so.3 compat symlink for Trixie
'src=$(find /usr/lib /lib -name "libfuse3.so.3.*" -type f 2>/dev/null ' \
'| sort -V | tail -1) ' \
'&& sudo ln -sfn "$src" "$(dirname "$src")/libfuse3.so.3" ' \
'&& sudo ldconfig',
'sudo touch /etc/fuse.conf ' \
'&& grep -qxF "user_allow_other" /etc/fuse.conf ' \
'|| echo "user_allow_other" | sudo tee -a /etc/fuse.conf'
)
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(name: 'fuse-azure', 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 ca-certificates curl gnupg wget").
// Microsoft's apt repo (use bookworm packages on Trixie)
Run("wget -qO- https://packages.microsoft.com/keys/microsoft.asc | " +
"sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg").
Run(`echo "deb [arch=$(dpkg --print-architecture) ` +
`signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] ` +
`https://packages.microsoft.com/debian/12/prod bookworm main" | ` +
`sudo tee /etc/apt/sources.list.d/microsoft-prod.list`).
Run("sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3").
// libfuse3.so.3 compat symlink for Trixie
Run(`src=$(find /usr/lib /lib -name "libfuse3.so.3.*" -type f 2>/dev/null | sort -V | tail -1) && ` +
`sudo ln -sfn "$src" "$(dirname "$src")/libfuse3.so.3" && sudo ldconfig`).
Run(`sudo touch /etc/fuse.conf && grep -qxF "user_allow_other" /etc/fuse.conf || ` +
`echo "user_allow_other" | sudo tee -a /etc/fuse.conf`)
_, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "fuse-azure",
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 ca-certificates curl gnupg wget",
// Microsoft's apt repo (use bookworm packages on Trixie)
"wget -qO- https://packages.microsoft.com/keys/microsoft.asc "
+ "| sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg",
"echo \"deb [arch=$(dpkg --print-architecture) "
+ "signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] "
+ "https://packages.microsoft.com/debian/12/prod bookworm main\" "
+ "| sudo tee /etc/apt/sources.list.d/microsoft-prod.list",
"sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3",
// libfuse3.so.3 compat symlink for Trixie
"src=$(find /usr/lib /lib -name \"libfuse3.so.3.*\" -type f 2>/dev/null "
+ "| sort -V | tail -1) "
+ "&& sudo ln -sfn \"$src\" \"$(dirname \"$src\")/libfuse3.so.3\" "
+ "&& sudo ldconfig",
"sudo touch /etc/fuse.conf "
+ "&& grep -qxF \"user_allow_other\" /etc/fuse.conf "
+ "|| echo \"user_allow_other\" | sudo tee -a /etc/fuse.conf"
);
daytona.snapshot().create("fuse-azure", image, System.out::println);
}
}
}
```
#### Launch and mount
`blobfuse2` reads its configuration from a YAML file. Build it with your account credentials and upload it into the sandbox.
The YAML below tells `blobfuse2` three things: **where to connect** (the `azstorage:` block — your storage account, the container you want to mount, the endpoint URL, and the auth method), **what to enable** (the `components:` list — the FUSE interface itself, a content cache, a metadata cache, and the Azure backend), and **how to log**. The cache components use sensible defaults when listed without their own top-level config blocks; add explicit `block_cache:` / `attr_cache:` blocks later if you need to tune cache sizes or timeouts. Note that in Azure terminology, a "container" is the equivalent of an S3 bucket — it's specified inside the YAML rather than passed as a command-line argument.
```python
import os
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
sandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot="fuse-azure"))
mount_path = "/home/daytona/azure"
config_path = "/home/daytona/.blobfuse2.yaml"
account = os.environ["AZURE_STORAGE_ACCOUNT"]
container = os.environ["AZURE_STORAGE_CONTAINER"]
account_key = os.environ["AZURE_STORAGE_ACCOUNT_KEY"]
config = f"""\
allow-other: true
logging:
type: syslog
level: log_warning
components:
- libfuse
- block_cache
- attr_cache
- azstorage
azstorage:
type: block
account-name: {account}
container: {container}
endpoint: https://{account}.blob.core.windows.net
auth-type: key
account-key: {account_key}
"""
sandbox.fs.upload_file(config.encode(), config_path)
sandbox.process.exec(f"chmod 600 {config_path}")
# Mount the container
sandbox.process.exec(f"mkdir -p {mount_path}")
sandbox.process.exec(f"blobfuse2 mount --config-file={config_path} {mount_path}")
# Use the mount
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-azure' })
const mountPath = '/home/daytona/azure'
const configPath = '/home/daytona/.blobfuse2.yaml'
const account = process.env.AZURE_STORAGE_ACCOUNT!
const container = process.env.AZURE_STORAGE_CONTAINER!
const accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY!
const config = `allow-other: true
logging:
type: syslog
level: log_warning
components:
- libfuse
- block_cache
- attr_cache
- azstorage
azstorage:
type: block
account-name: ${account}
container: ${container}
endpoint: https://${account}.blob.core.windows.net
auth-type: key
account-key: ${accountKey}
`
await sandbox.fs.uploadFile(Buffer.from(config), configPath)
await sandbox.process.executeCommand(`chmod 600 ${configPath}`)
// Mount the container
await sandbox.process.executeCommand(`mkdir -p ${mountPath}`)
await sandbox.process.executeCommand(`blobfuse2 mount --config-file=${configPath} ${mountPath}`)
// Use the mount
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-azure')
)
mount_path = '/home/daytona/azure'
config_path = '/home/daytona/.blobfuse2.yaml'
account = ENV.fetch('AZURE_STORAGE_ACCOUNT')
container = ENV.fetch('AZURE_STORAGE_CONTAINER')
account_key = ENV.fetch('AZURE_STORAGE_ACCOUNT_KEY')
config = <<~YAML
allow-other: true
logging:
type: syslog
level: log_warning
components:
- libfuse
- block_cache
- attr_cache
- azstorage
azstorage:
type: block
account-name: #{account}
container: #{container}
endpoint: https://#{account}.blob.core.windows.net
auth-type: key
account-key: #{account_key}
YAML
sandbox.fs.upload_file(config, config_path)
sandbox.process.exec(command: "chmod 600 #{config_path}")
# Mount the container
sandbox.process.exec(command: "mkdir -p #{mount_path}")
sandbox.process.exec(command: "blobfuse2 mount --config-file=#{config_path} #{mount_path}")
# Use the mount
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-azure",
})
if err != nil {
log.Fatal(err)
}
mountPath := "/home/daytona/azure"
configPath := "/home/daytona/.blobfuse2.yaml"
account := os.Getenv("AZURE_STORAGE_ACCOUNT")
container := os.Getenv("AZURE_STORAGE_CONTAINER")
accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY")
config := fmt.Sprintf(`allow-other: true
logging:
type: syslog
level: log_warning
components:
- libfuse
- block_cache
- attr_cache
- azstorage
azstorage:
type: block
account-name: %s
container: %s
endpoint: https://%s.blob.core.windows.net
auth-type: key
account-key: %s
`, account, container, account, accountKey)
if err := sandbox.FileSystem.UploadFile(ctx, []byte(config), configPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "chmod 600 "+configPath); err != nil {
log.Fatal(err)
}
// Mount the container
if _, err := sandbox.Process.ExecuteCommand(ctx, "mkdir -p "+mountPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
"blobfuse2 mount --config-file="+configPath+" "+mountPath); err != nil {
log.Fatal(err)
}
// Use the mount
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.nio.charset.StandardCharsets;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("fuse-azure");
Sandbox sandbox = daytona.create(params);
String mountPath = "/home/daytona/azure";
String configPath = "/home/daytona/.blobfuse2.yaml";
String account = System.getenv("AZURE_STORAGE_ACCOUNT");
String container = System.getenv("AZURE_STORAGE_CONTAINER");
String accountKey = System.getenv("AZURE_STORAGE_ACCOUNT_KEY");
String config = "allow-other: true\n"
+ "logging:\n"
+ " type: syslog\n"
+ " level: log_warning\n"
+ "components:\n"
+ " - libfuse\n"
+ " - block_cache\n"
+ " - attr_cache\n"
+ " - azstorage\n"
+ "azstorage:\n"
+ " type: block\n"
+ " account-name: " + account + "\n"
+ " container: " + container + "\n"
+ " endpoint: https://" + account + ".blob.core.windows.net\n"
+ " auth-type: key\n"
+ " account-key: " + accountKey + "\n";
sandbox.fs.uploadFile(config.getBytes(StandardCharsets.UTF_8), configPath);
sandbox.getProcess().executeCommand("chmod 600 " + configPath);
// Mount the container
sandbox.getProcess().executeCommand("mkdir -p " + mountPath);
sandbox.getProcess().executeCommand(
"blobfuse2 mount --config-file=" + configPath + " " + mountPath);
// Use the mount
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
### Runtime install
Start from a default sandbox and install `blobfuse2` during startup before writing the config and mounting the container. This is useful for quick validation and experiments, with the tradeoff of slower cold starts and repeated setup on each sandbox launch.
```python
import os
from daytona import CreateSandboxBaseParams, Daytona
daytona = Daytona()
sandbox = daytona.create(CreateSandboxBaseParams())
# Install blobfuse2
sandbox.process.exec(
"sudo apt-get update "
"&& sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg wget"
)
sandbox.process.exec(
"wget -qO- https://packages.microsoft.com/keys/microsoft.asc "
"| sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg"
)
sandbox.process.exec(
'echo "deb [arch=$(dpkg --print-architecture) '
'signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] '
'https://packages.microsoft.com/debian/12/prod bookworm main" '
"| sudo tee /etc/apt/sources.list.d/microsoft-prod.list "
"&& sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3"
)
# libfuse3.so.3 compat symlink for Trixie
sandbox.process.exec(
'src=$(find /usr/lib /lib -name "libfuse3.so.3.*" -type f 2>/dev/null '
"| sort -V | tail -1) "
'&& sudo ln -sfn "$src" "$(dirname "$src")/libfuse3.so.3" '
"&& sudo ldconfig"
)
# Build config and mount
mount_path = "/home/daytona/azure"
config_path = "/home/daytona/.blobfuse2.yaml"
account = os.environ["AZURE_STORAGE_ACCOUNT"]
container = os.environ["AZURE_STORAGE_CONTAINER"]
account_key = os.environ["AZURE_STORAGE_ACCOUNT_KEY"]
config = f"""\
allow-other: true
components:
- libfuse
- block_cache
- attr_cache
- azstorage
azstorage:
type: block
account-name: {account}
container: {container}
endpoint: https://{account}.blob.core.windows.net
auth-type: key
account-key: {account_key}
"""
sandbox.fs.upload_file(config.encode(), config_path)
sandbox.process.exec(f"chmod 600 {config_path}")
sandbox.process.exec(f"mkdir -p {mount_path}")
sandbox.process.exec(f"blobfuse2 mount --config-file={config_path} {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()
// Install blobfuse2
await sandbox.process.executeCommand(
'sudo apt-get update ' +
'&& sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg wget',
)
await sandbox.process.executeCommand(
'wget -qO- https://packages.microsoft.com/keys/microsoft.asc ' +
'| sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg',
)
await sandbox.process.executeCommand(
'echo "deb [arch=$(dpkg --print-architecture) ' +
'signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] ' +
'https://packages.microsoft.com/debian/12/prod bookworm main" ' +
'| sudo tee /etc/apt/sources.list.d/microsoft-prod.list ' +
'&& sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3',
)
// libfuse3.so.3 compat symlink for Trixie
await sandbox.process.executeCommand(
'src=$(find /usr/lib /lib -name "libfuse3.so.3.*" -type f 2>/dev/null ' +
'| sort -V | tail -1) ' +
'&& sudo ln -sfn "$src" "$(dirname "$src")/libfuse3.so.3" ' +
'&& sudo ldconfig',
)
// Build config and mount
const mountPath = '/home/daytona/azure'
const configPath = '/home/daytona/.blobfuse2.yaml'
const account = process.env.AZURE_STORAGE_ACCOUNT!
const container = process.env.AZURE_STORAGE_CONTAINER!
const accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY!
const config = `allow-other: true
components:
- libfuse
- block_cache
- attr_cache
- azstorage
azstorage:
type: block
account-name: ${account}
container: ${container}
endpoint: https://${account}.blob.core.windows.net
auth-type: key
account-key: ${accountKey}
`
await sandbox.fs.uploadFile(Buffer.from(config), configPath)
await sandbox.process.executeCommand(`chmod 600 ${configPath}`)
await sandbox.process.executeCommand(`mkdir -p ${mountPath}`)
await sandbox.process.executeCommand(`blobfuse2 mount --config-file=${configPath} ${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)
# Install blobfuse2
sandbox.process.exec(
command: 'sudo apt-get update ' \
'&& sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg wget'
)
sandbox.process.exec(
command: 'wget -qO- https://packages.microsoft.com/keys/microsoft.asc ' \
'| sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg'
)
sandbox.process.exec(
command: 'echo "deb [arch=$(dpkg --print-architecture) ' \
'signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] ' \
'https://packages.microsoft.com/debian/12/prod bookworm main" ' \
'| sudo tee /etc/apt/sources.list.d/microsoft-prod.list ' \
'&& sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3'
)
# libfuse3.so.3 compat symlink for Trixie
sandbox.process.exec(
command: 'src=$(find /usr/lib /lib -name "libfuse3.so.3.*" -type f 2>/dev/null ' \
'| sort -V | tail -1) ' \
'&& sudo ln -sfn "$src" "$(dirname "$src")/libfuse3.so.3" ' \
'&& sudo ldconfig'
)
# Build config and mount
mount_path = '/home/daytona/azure'
config_path = '/home/daytona/.blobfuse2.yaml'
account = ENV.fetch('AZURE_STORAGE_ACCOUNT')
container = ENV.fetch('AZURE_STORAGE_CONTAINER')
account_key = ENV.fetch('AZURE_STORAGE_ACCOUNT_KEY')
config = <<~YAML
allow-other: true
components:
- libfuse
- block_cache
- attr_cache
- azstorage
azstorage:
type: block
account-name: #{account}
container: #{container}
endpoint: https://#{account}.blob.core.windows.net
auth-type: key
account-key: #{account_key}
YAML
sandbox.fs.upload_file(config, config_path)
sandbox.process.exec(command: "chmod 600 #{config_path}")
sandbox.process.exec(command: "mkdir -p #{mount_path}")
sandbox.process.exec(command: "blobfuse2 mount --config-file=#{config_path} #{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{})
if err != nil {
log.Fatal(err)
}
// Install blobfuse2
if _, err := sandbox.Process.ExecuteCommand(ctx,
"sudo apt-get update && sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg wget"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
"wget -qO- https://packages.microsoft.com/keys/microsoft.asc | "+
"sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
`echo "deb [arch=$(dpkg --print-architecture) `+
`signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] `+
`https://packages.microsoft.com/debian/12/prod bookworm main" | `+
`sudo tee /etc/apt/sources.list.d/microsoft-prod.list && `+
`sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3`); err != nil {
log.Fatal(err)
}
// libfuse3.so.3 compat symlink for Trixie
if _, err := sandbox.Process.ExecuteCommand(ctx,
`src=$(find /usr/lib /lib -name "libfuse3.so.3.*" -type f 2>/dev/null | sort -V | tail -1) && `+
`sudo ln -sfn "$src" "$(dirname "$src")/libfuse3.so.3" && sudo ldconfig`); err != nil {
log.Fatal(err)
}
// Build config and mount
mountPath := "/home/daytona/azure"
configPath := "/home/daytona/.blobfuse2.yaml"
account := os.Getenv("AZURE_STORAGE_ACCOUNT")
container := os.Getenv("AZURE_STORAGE_CONTAINER")
accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY")
config := fmt.Sprintf(`allow-other: true
components:
- libfuse
- block_cache
- attr_cache
- azstorage
azstorage:
type: block
account-name: %s
container: %s
endpoint: https://%s.blob.core.windows.net
auth-type: key
account-key: %s
`, account, container, account, accountKey)
if err := sandbox.FileSystem.UploadFile(ctx, []byte(config), configPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "chmod 600 "+configPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "mkdir -p "+mountPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
"blobfuse2 mount --config-file="+configPath+" "+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.nio.charset.StandardCharsets;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
Sandbox sandbox = daytona.create(new CreateSandboxFromSnapshotParams());
// Install blobfuse2
sandbox.getProcess().executeCommand(
"sudo apt-get update "
+ "&& sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg wget");
sandbox.getProcess().executeCommand(
"wget -qO- https://packages.microsoft.com/keys/microsoft.asc "
+ "| sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg");
sandbox.getProcess().executeCommand(
"echo \"deb [arch=$(dpkg --print-architecture) "
+ "signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] "
+ "https://packages.microsoft.com/debian/12/prod bookworm main\" "
+ "| sudo tee /etc/apt/sources.list.d/microsoft-prod.list "
+ "&& sudo apt-get update && sudo apt-get install -y blobfuse2 fuse3");
// libfuse3.so.3 compat symlink for Trixie
sandbox.getProcess().executeCommand(
"src=$(find /usr/lib /lib -name \"libfuse3.so.3.*\" -type f 2>/dev/null "
+ "| sort -V | tail -1) "
+ "&& sudo ln -sfn \"$src\" \"$(dirname \"$src\")/libfuse3.so.3\" "
+ "&& sudo ldconfig");
// Build config and mount
String mountPath = "/home/daytona/azure";
String configPath = "/home/daytona/.blobfuse2.yaml";
String account = System.getenv("AZURE_STORAGE_ACCOUNT");
String container = System.getenv("AZURE_STORAGE_CONTAINER");
String accountKey = System.getenv("AZURE_STORAGE_ACCOUNT_KEY");
String config = "allow-other: true\n"
+ "components:\n"
+ " - libfuse\n"
+ " - block_cache\n"
+ " - attr_cache\n"
+ " - azstorage\n"
+ "azstorage:\n"
+ " type: block\n"
+ " account-name: " + account + "\n"
+ " container: " + container + "\n"
+ " endpoint: https://" + account + ".blob.core.windows.net\n"
+ " auth-type: key\n"
+ " account-key: " + accountKey + "\n";
sandbox.fs.uploadFile(config.getBytes(StandardCharsets.UTF_8), configPath);
sandbox.getProcess().executeCommand("chmod 600 " + configPath);
sandbox.getProcess().executeCommand("mkdir -p " + mountPath);
sandbox.getProcess().executeCommand(
"blobfuse2 mount --config-file=" + configPath + " " + mountPath);
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
## Mount a MesaFS filesystem
[MesaFS ↗](https://mesa.dev) is an agent-native versioned filesystem from Mesa, purpose-built for the same workloads Daytona sandboxes run — parallel agent swarms, shared working memory, structured artifacts, and long-lived state across runs. With MesaFS, instead of mounting a cloud bucket, you mount a Mesa **repository**: a Git-compatible versioned working directory with sub-50ms reads/writes, instant fork/branch operations, and unlimited concurrent writers.
The Mesa setup follows the same pattern as the bucket providers but uses the Mesa CLI rather than a FUSE-specific tool: install the CLI in your sandbox, authenticate with your API key, and run `mesa mount --daemonize` to mount your repos at `/home/daytona/mesa/mnt//`.
**Credentials** — set `MESA_API_KEY` and `MESA_ORG` (your Mesa organization slug) in your local environment. The snippets below pass them into the sandbox via `envVars`, and the Mesa CLI reads them from there.
### Pre-built snapshot
Build a snapshot with the Mesa CLI preinstalled, then launch Mesa-enabled sandboxes from that snapshot. You still authenticate and mount at runtime, but installation is no longer part of each sandbox startup sequence.
#### Build a snapshot
Create a reusable snapshot that installs the Mesa CLI and enables the FUSE `user_allow_other` setting. Sandboxes launched from `fuse-mesa` can then authenticate and mount repos without repeating install work.
```python
from daytona import CreateSnapshotParams, Daytona, Image
daytona = Daytona()
image = (
Image.base("daytonaio/sandbox")
.run_commands(
"curl -fsSL https://mesa.dev/install.sh | sh",
"sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf",
)
)
daytona.snapshot.create(
CreateSnapshotParams(name="fuse-mesa", image=image),
on_logs=lambda chunk: print(chunk, end="", flush=True),
)
```
```typescript
import { Daytona, Image } from '@daytona/sdk'
const daytona = new Daytona()
const image = Image.base('daytonaio/sandbox').runCommands(
'curl -fsSL https://mesa.dev/install.sh | sh',
"sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf",
)
await daytona.snapshot.create(
{ name: 'fuse-mesa', image },
{ onLogs: console.log },
)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
image = Daytona::Image
.base('daytonaio/sandbox')
.run_commands(
'curl -fsSL https://mesa.dev/install.sh | sh',
"sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf"
)
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(name: 'fuse-mesa', 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("curl -fsSL https://mesa.dev/install.sh | sh").
Run(`sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf`)
_, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{
Name: "fuse-mesa",
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(
"curl -fsSL https://mesa.dev/install.sh | sh",
"sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf"
);
daytona.snapshot().create("fuse-mesa", image, System.out::println);
}
}
}
```
#### Launch and mount
Pass `MESA_API_KEY` and your Mesa organization slug to the sandbox via `envVars`. Your code then writes a TOML config into the sandbox, authenticates the Mesa CLI, and mounts your repos at `/home/daytona/mesa/mnt//`.
```python
import os
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
org = os.environ["MESA_ORG"]
repo = "my-workspace"
mount_path = f"/home/daytona/mesa/mnt/{org}/{repo}"
config_path = "/home/daytona/.config/mesa/config.toml"
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="fuse-mesa",
env_vars={
"MESA_API_KEY": os.environ["MESA_API_KEY"],
"MESA_ORG": org,
},
)
)
config = f'''mount-point = "/home/daytona/mesa/mnt"
[secrets]
backend = "plaintext-file"
[organizations.{org}]
'''
sandbox.process.exec(f"mkdir -p $(dirname {config_path})")
sandbox.fs.upload_file(config.encode(), config_path)
sandbox.process.exec("mesa auth set-key --org $MESA_ORG $MESA_API_KEY")
sandbox.process.exec("mesa mount --daemonize")
response = sandbox.process.exec(f"ls {mount_path}")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const org = process.env.MESA_ORG!
const repo = 'my-workspace'
const mountPath = `/home/daytona/mesa/mnt/${org}/${repo}`
const configPath = '/home/daytona/.config/mesa/config.toml'
const sandbox = await daytona.create({
snapshot: 'fuse-mesa',
envVars: {
MESA_API_KEY: process.env.MESA_API_KEY!,
MESA_ORG: org,
},
})
const config = `mount-point = "/home/daytona/mesa/mnt"
[secrets]
backend = "plaintext-file"
[organizations.${org}]
`
await sandbox.process.executeCommand(`mkdir -p $(dirname ${configPath})`)
await sandbox.fs.uploadFile(Buffer.from(config), configPath)
await sandbox.process.executeCommand('mesa auth set-key --org $MESA_ORG $MESA_API_KEY')
await sandbox.process.executeCommand('mesa mount --daemonize')
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
org = ENV.fetch('MESA_ORG')
repo = 'my-workspace'
mount_path = "/home/daytona/mesa/mnt/#{org}/#{repo}"
config_path = '/home/daytona/.config/mesa/config.toml'
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(
snapshot: 'fuse-mesa',
env_vars: {
'MESA_API_KEY' => ENV.fetch('MESA_API_KEY'),
'MESA_ORG' => org
}
)
)
config = <<~TOML
mount-point = "/home/daytona/mesa/mnt"
[secrets]
backend = "plaintext-file"
[organizations.#{org}]
TOML
sandbox.process.exec(command: "mkdir -p $(dirname #{config_path})")
sandbox.fs.upload_file(config, config_path)
sandbox.process.exec(command: 'mesa auth set-key --org $MESA_ORG $MESA_API_KEY')
sandbox.process.exec(command: 'mesa mount --daemonize')
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)
}
org := os.Getenv("MESA_ORG")
repo := "my-workspace"
mountPath := fmt.Sprintf("/home/daytona/mesa/mnt/%s/%s", org, repo)
configPath := "/home/daytona/.config/mesa/config.toml"
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: "fuse-mesa",
SandboxBaseParams: types.SandboxBaseParams{
EnvVars: map[string]string{
"MESA_API_KEY": os.Getenv("MESA_API_KEY"),
"MESA_ORG": org,
},
},
})
if err != nil {
log.Fatal(err)
}
config := fmt.Sprintf(`mount-point = "/home/daytona/mesa/mnt"
[secrets]
backend = "plaintext-file"
[organizations.%s]
`, org)
if _, err := sandbox.Process.ExecuteCommand(ctx, "mkdir -p $(dirname "+configPath+")"); err != nil {
log.Fatal(err)
}
if err := sandbox.FileSystem.UploadFile(ctx, []byte(config), configPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "mesa auth set-key --org $MESA_ORG $MESA_API_KEY"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "mesa mount --daemonize"); 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.nio.charset.StandardCharsets;
import java.util.Map;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
String org = System.getenv("MESA_ORG");
String repo = "my-workspace";
String mountPath = "/home/daytona/mesa/mnt/" + org + "/" + repo;
String configPath = "/home/daytona/.config/mesa/config.toml";
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setSnapshot("fuse-mesa");
params.setEnvVars(Map.of(
"MESA_API_KEY", System.getenv("MESA_API_KEY"),
"MESA_ORG", org
));
Sandbox sandbox = daytona.create(params);
String config = "mount-point = \"/home/daytona/mesa/mnt\"\n\n"
+ "[secrets]\n"
+ "backend = \"plaintext-file\"\n\n"
+ "[organizations." + org + "]\n";
sandbox.getProcess().executeCommand("mkdir -p $(dirname " + configPath + ")");
sandbox.fs.uploadFile(config.getBytes(StandardCharsets.UTF_8), configPath);
sandbox.getProcess().executeCommand(
"mesa auth set-key --org $MESA_ORG $MESA_API_KEY");
sandbox.getProcess().executeCommand("mesa mount --daemonize");
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
### Runtime install
Start from a default sandbox and install the Mesa CLI during startup before configuring auth and running `mesa mount --daemonize`. This is useful when iterating quickly on mount behavior, with the tradeoff of slower cold starts for each sandbox.
```python
import os
from daytona import CreateSandboxBaseParams, Daytona
daytona = Daytona()
org = os.environ["MESA_ORG"]
repo = "my-workspace"
mount_path = f"/home/daytona/mesa/mnt/{org}/{repo}"
config_path = "/home/daytona/.config/mesa/config.toml"
sandbox = daytona.create(
CreateSandboxBaseParams(
env_vars={
"MESA_API_KEY": os.environ["MESA_API_KEY"],
"MESA_ORG": org,
},
)
)
sandbox.process.exec("curl -fsSL https://mesa.dev/install.sh | sh")
sandbox.process.exec(
"sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf"
)
config = f'''mount-point = "/home/daytona/mesa/mnt"
[secrets]
backend = "plaintext-file"
[organizations.{org}]
'''
sandbox.process.exec(f"mkdir -p $(dirname {config_path})")
sandbox.fs.upload_file(config.encode(), config_path)
sandbox.process.exec("mesa auth set-key --org $MESA_ORG $MESA_API_KEY")
sandbox.process.exec("mesa mount --daemonize")
response = sandbox.process.exec(f"ls {mount_path}")
print(response.result)
```
```typescript
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
const org = process.env.MESA_ORG!
const repo = 'my-workspace'
const mountPath = `/home/daytona/mesa/mnt/${org}/${repo}`
const configPath = '/home/daytona/.config/mesa/config.toml'
const sandbox = await daytona.create({
envVars: {
MESA_API_KEY: process.env.MESA_API_KEY!,
MESA_ORG: org,
},
})
await sandbox.process.executeCommand('curl -fsSL https://mesa.dev/install.sh | sh')
await sandbox.process.executeCommand(
"sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf",
)
const config = `mount-point = "/home/daytona/mesa/mnt"
[secrets]
backend = "plaintext-file"
[organizations.${org}]
`
await sandbox.process.executeCommand(`mkdir -p $(dirname ${configPath})`)
await sandbox.fs.uploadFile(Buffer.from(config), configPath)
await sandbox.process.executeCommand('mesa auth set-key --org $MESA_ORG $MESA_API_KEY')
await sandbox.process.executeCommand('mesa mount --daemonize')
const response = await sandbox.process.executeCommand(`ls ${mountPath}`)
console.log(response.result)
```
```ruby
require 'daytona'
daytona = Daytona::Daytona.new
org = ENV.fetch('MESA_ORG')
repo = 'my-workspace'
mount_path = "/home/daytona/mesa/mnt/#{org}/#{repo}"
config_path = '/home/daytona/.config/mesa/config.toml'
sandbox = daytona.create(
Daytona::CreateSandboxBaseParams.new(
env_vars: {
'MESA_API_KEY' => ENV.fetch('MESA_API_KEY'),
'MESA_ORG' => org
}
)
)
sandbox.process.exec(command: 'curl -fsSL https://mesa.dev/install.sh | sh')
sandbox.process.exec(
command: "sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf"
)
config = <<~TOML
mount-point = "/home/daytona/mesa/mnt"
[secrets]
backend = "plaintext-file"
[organizations.#{org}]
TOML
sandbox.process.exec(command: "mkdir -p $(dirname #{config_path})")
sandbox.fs.upload_file(config, config_path)
sandbox.process.exec(command: 'mesa auth set-key --org $MESA_ORG $MESA_API_KEY')
sandbox.process.exec(command: 'mesa mount --daemonize')
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)
}
org := os.Getenv("MESA_ORG")
repo := "my-workspace"
mountPath := fmt.Sprintf("/home/daytona/mesa/mnt/%s/%s", org, repo)
configPath := "/home/daytona/.config/mesa/config.toml"
sandbox, err := client.Create(ctx, types.SnapshotParams{
SandboxBaseParams: types.SandboxBaseParams{
EnvVars: map[string]string{
"MESA_API_KEY": os.Getenv("MESA_API_KEY"),
"MESA_ORG": org,
},
},
})
if err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
"curl -fsSL https://mesa.dev/install.sh | sh"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx,
`sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf`); err != nil {
log.Fatal(err)
}
config := fmt.Sprintf(`mount-point = "/home/daytona/mesa/mnt"
[secrets]
backend = "plaintext-file"
[organizations.%s]
`, org)
if _, err := sandbox.Process.ExecuteCommand(ctx, "mkdir -p $(dirname "+configPath+")"); err != nil {
log.Fatal(err)
}
if err := sandbox.FileSystem.UploadFile(ctx, []byte(config), configPath); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "mesa auth set-key --org $MESA_ORG $MESA_API_KEY"); err != nil {
log.Fatal(err)
}
if _, err := sandbox.Process.ExecuteCommand(ctx, "mesa mount --daemonize"); 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.nio.charset.StandardCharsets;
import java.util.Map;
public class App {
public static void main(String[] args) {
try (Daytona daytona = new Daytona()) {
String org = System.getenv("MESA_ORG");
String repo = "my-workspace";
String mountPath = "/home/daytona/mesa/mnt/" + org + "/" + repo;
String configPath = "/home/daytona/.config/mesa/config.toml";
CreateSandboxFromSnapshotParams params = new CreateSandboxFromSnapshotParams();
params.setEnvVars(Map.of(
"MESA_API_KEY", System.getenv("MESA_API_KEY"),
"MESA_ORG", org
));
Sandbox sandbox = daytona.create(params);
sandbox.getProcess().executeCommand(
"curl -fsSL https://mesa.dev/install.sh | sh");
sandbox.getProcess().executeCommand(
"sudo sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf");
String config = "mount-point = \"/home/daytona/mesa/mnt\"\n\n"
+ "[secrets]\n"
+ "backend = \"plaintext-file\"\n\n"
+ "[organizations." + org + "]\n";
sandbox.getProcess().executeCommand("mkdir -p $(dirname " + configPath + ")");
sandbox.fs.uploadFile(config.getBytes(StandardCharsets.UTF_8), configPath);
sandbox.getProcess().executeCommand(
"mesa auth set-key --org $MESA_ORG $MESA_API_KEY");
sandbox.getProcess().executeCommand("mesa mount --daemonize");
ExecuteResponse response = sandbox.getProcess().executeCommand("ls " + mountPath);
System.out.println(response.getResult());
}
}
}
```
### Production: scoped ephemeral keys
For non-test workloads, Mesa recommends minting a **short-lived, scoped API key per sandbox session** rather than passing your long-lived `MESA_API_KEY` into the sandbox. Use the [Mesa SDK ↗](https://docs.mesa.dev) on your trusted host to derive an ephemeral key from your long-lived one — the long-lived key never leaves your host process. Mesa SDKs are available for TypeScript, Python, and Rust; for other languages, use the [Mesa REST API ↗](https://docs.mesa.dev/content/api-reference/overview) directly.
```python
import asyncio
import os
from daytona import CreateSandboxFromSnapshotParams, Daytona
from mesa_sdk import Mesa
async def mint_ephemeral_key() -> str:
async with Mesa(api_key=os.environ["MESA_API_KEY"], org=os.environ["MESA_ORG"]) as mesa:
key = await mesa.api_keys.create(
name="sandbox-session",
scopes=["read", "write"],
expires_in_seconds=3600,
)
return key.key
ephemeral_key = asyncio.run(mint_ephemeral_key())
daytona = Daytona()
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(
snapshot="fuse-mesa",
env_vars={
"MESA_API_KEY": ephemeral_key,
"MESA_ORG": os.environ["MESA_ORG"],
},
)
)
```
```typescript
import { Daytona } from '@daytona/sdk'
import { Mesa } from '@mesadev/sdk'
const mesa = new Mesa({ apiKey: process.env.MESA_API_KEY!, org: process.env.MESA_ORG! })
const ephemeralKey = await mesa.apiKeys.create({
name: 'sandbox-session',
scopes: ['read', 'write'],
expires_in_seconds: 3600,
})
const daytona = new Daytona()
const sandbox = await daytona.create({
snapshot: 'fuse-mesa',
envVars: {
MESA_API_KEY: ephemeralKey.key,
MESA_ORG: process.env.MESA_ORG!,
},
})
```
The rest of the launch flow (writing the TOML config, `mesa auth set-key`, `mesa mount --daemonize`) is unchanged — the sandbox doesn't know whether the key it received is long-lived or ephemeral.
For repo-scoped or path-scoped keys, see Mesa's [auth and permissions guide ↗](https://docs.mesa.dev/content/getting-started/auth-and-permissions). For the full integration recipe, see Mesa's [Daytona guide ↗](https://docs.mesa.dev/content/integration-guides/daytona).
## Unmount
When a sandbox is deleted via `daytona.delete(sandbox)`, the container teardown automatically removes any active FUSE mounts and shuts down their daemons. For normal cleanup, this is all you need — no manual unmount required.
To free a mount path **during** a sandbox's lifetime (for example, to remount with different credentials or before persisting a workspace archive), relocate the mount onto a throwaway path:
```bash
sudo mkdir -p /tmp/.fuse-defunct-$$
sudo mount --move /tmp/.fuse-defunct-$$
```
After this, your original mount path is free for remounting. The FUSE daemon stays alive serving the mount at the new path; both the relocated mount and the daemon are cleaned up automatically when the sandbox is deleted.
This works for any FUSE-based mount — verified against `mount-s3`, `gcsfuse`, and `blobfuse2`.
# Regions
Sandboxes are isolated runtime environments that run on [runners](https://www.daytona.io/docs/en/runners.md) — machines that form Daytona's compute plane.
Runners are organized into **regions**, which are geographic or logical groupings of compute infrastructure. When creating a sandbox, you can target a specific region, and Daytona will schedule your workload on an available runner within that region.
As a result, you're able to:
- Choose specific geographic locations for reduced latency
- Comply with data residency requirements
- Use your own runner machines for custom regions
- Scale compute resources independently within each custom region
Regions are geographic or logical groupings of runners that execute sandbox workloads. The sandbox region is specified by setting the `target` parameter on initialization:
```python
from daytona import Daytona, DaytonaConfig
# Configure Daytona to use the US region
config = DaytonaConfig(
target="us"
)
# Initialize the Daytona client with the specified configuration
daytona = Daytona(config)
```
```typescript
import { Daytona } from '@daytona/sdk';
// Configure Daytona to use the EU region
const daytona: Daytona = new Daytona({
target: "eu"
});
```
```go
package main
import (
"github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona"
"github.com/daytonaio/daytona/libs/sdk-go/pkg/types"
)
// Configure Daytona to use the US region
client, _ := daytona.NewClientWithConfig(&types.DaytonaConfig{
Target: "us",
})
```
```ruby
require 'daytona'
# Configure Daytona to use the EU region
config = Daytona::Config.new(
target: "eu"
)
# Initialize the Daytona client with the specified configuration
daytona = Daytona::Daytona.new(config)
```
```java
import io.daytona.sdk.Daytona;
import io.daytona.sdk.DaytonaConfig;
// Configure Daytona to use the US region
DaytonaConfig config = new DaytonaConfig.Builder()
.apiKey(System.getenv("DAYTONA_API_KEY"))
.target("us")
.build();
Daytona daytona = new Daytona(config);
```
### Shared regions
Shared regions are managed by Daytona and available to all organizations. These regions provide immediate access to Daytona's infrastructure without any setup required.
Limits are applied to your organization's default region. For access to a different shared region, contact [sales@daytona.io](mailto:sales@daytona.io).
| Region | Target |
| ------------- | -------- |
| United States | **`us`** |
| Europe | **`eu`** |
### Dedicated regions
Dedicated regions are managed by Daytona and provisioned exclusively for individual organizations. These regions deliver dedicated infrastructure with the operational simplicity of a managed service.
:::note
Contact [sales@daytona.io](mailto:sales@daytona.io) to set up a dedicated region for your organization.
:::
### Custom regions
Custom regions are created and managed by your organization, allowing you to use your own runner machines and scale compute resources independently within each region. This provides maximum control over data locality, compliance, and infrastructure configuration.
Additionally, custom regions have no limits applied for concurrent resource usage, giving you full control over capacity and performance.
For more information, see the [runners](https://www.daytona.io/docs/en/runners.md) guide.
# File System Operations
Daytona provides comprehensive file system operations through the `fs` module in sandboxes.
## Basic operations
Daytona provides methods to interact with the file system in sandboxes. You can perform various operations like listing files, creating directories, reading and writing files, and more.
File operations assume you are operating in the sandbox user's home directory (e.g. `workspace` implies `/home/[username]/workspace`). Use a leading `/` when providing absolute paths.
### List files and directories
Daytona provides methods to list files and directories in a sandbox by providing the path to the directory. If the path is not provided, the method will list the files and directories in the sandbox working directory.
```python
# List files in a directory
files = sandbox.fs.list_files("workspace")
for file in files:
print(f"Name: {file.name}")
print(f"Is directory: {file.is_dir}")
print(f"Size: {file.size}")
print(f"Modified: {file.mod_time}")
```
```typescript
// List files in a directory
const files = await sandbox.fs.listFiles('workspace')
files.forEach(file => {
console.log(`Name: ${file.name}`)
console.log(`Is directory: ${file.isDir}`)
console.log(`Size: ${file.size}`)
console.log(`Modified: ${file.modTime}`)
})
```
```ruby
# List directory contents
files = sandbox.fs.list_files("workspace/data")
# Print files and their sizes
files.each do |file|
puts "#{file.name}: #{file.size} bytes" unless file.is_dir
end
# List only directories
dirs = files.select(&:is_dir)
puts "Subdirectories: #{dirs.map(&:name).join(', ')}"
```
```go
// List files in a directory
files, err := sandbox.FileSystem.ListFiles(ctx, "workspace")
if err != nil {
log.Fatal(err)
}
for _, file := range files {
fmt.Printf("Name: %s\n", file.Name)
fmt.Printf("Is directory: %t\n", file.IsDirectory)
fmt.Printf("Size: %d\n", file.Size)
fmt.Printf("Modified: %s\n", file.ModifiedTime)
}
```
```java
import io.daytona.sdk.model.FileInfo;
import java.util.List;
List files = sandbox.fs.listFiles("workspace");
for (FileInfo file : files) {
System.out.println("Name: " + file.getName());
System.out.println("Is directory: " + file.getIsDir());
System.out.println("Size: " + file.getSize());
System.out.println("Modified: " + file.getModTime());
}
```
```bash
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files'
```
### Get directory or file information
Daytona provides methods to get directory or file information such as group, directory, modified time, mode, name, owner, permissions, and size by providing the path to the directory or file.
```python
# Get file metadata
info = sandbox.fs.get_file_info("workspace/data/file.txt")
print(f"Size: {info.size} bytes")
print(f"Modified: {info.mod_time}")
print(f"Mode: {info.mode}")
# Check if path is a directory
info = sandbox.fs.get_file_info("workspace/data")
if info.is_dir:
print("Path is a directory")
```
```typescript
// Get file details
const info = await fs.getFileDetails('app/config.json')
console.log(`Size: ${info.size}, Modified: ${info.modTime}`)
```
```ruby
# Get file metadata
info = sandbox.fs.get_file_info("workspace/data/file.txt")
puts "Size: #{info.size} bytes"
puts "Modified: #{info.mod_time}"
puts "Mode: #{info.mode}"
# Check if path is a directory
info = sandbox.fs.get_file_info("workspace/data")
puts "Path is a directory" if info.is_dir
```
```go
// Get file metadata
info, err := sandbox.FileSystem.GetFileInfo(ctx, "workspace/data/file.txt")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Size: %d bytes\n", info.Size)
fmt.Printf("Modified: %s\n", info.ModifiedTime)
fmt.Printf("Mode: %s\n", info.Mode)
// Check if path is a directory
info, err = sandbox.FileSystem.GetFileInfo(ctx, "workspace/data")
if err != nil {
log.Fatal(err)
}
if info.IsDirectory {
fmt.Println("Path is a directory")
}
```
```java
import io.daytona.sdk.model.FileInfo;
FileInfo info = sandbox.fs.getFileDetails("workspace/data/file.txt");
System.out.println("Size: " + info.getSize() + " bytes");
System.out.println("Modified: " + info.getModTime());
System.out.println("Mode: " + info.getMode());
info = sandbox.fs.getFileDetails("workspace/data");
if (Boolean.TRUE.equals(info.getIsDir())) {
System.out.println("Path is a directory");
}
```
```bash
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/info?path='
```
### Create directories
Daytona provides methods to create directories by providing the path to the directory and the permissions to set on the directory.
```python
# Create with specific permissions
sandbox.fs.create_folder("workspace/new-dir", "755")
```
```typescript
// Create with specific permissions
await sandbox.fs.createFolder('workspace/new-dir', '755')
```
```ruby
# Create a directory with standard permissions
sandbox.fs.create_folder("workspace/data", "755")
# Create a private directory
sandbox.fs.create_folder("workspace/secrets", "700")
```
```go
// Create with specific permissions
err := sandbox.FileSystem.CreateFolder(ctx, "workspace/new-dir",
options.WithMode("755"),
)
if err != nil {
log.Fatal(err)
}
```
```java
sandbox.fs.createFolder("workspace/new-dir", "755");
```
```bash
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/folder?path=&mode=' \
--request POST
```
### Upload files
Daytona provides methods to upload a single or multiple files in sandboxes.
#### Upload a single file
Daytona provides methods to upload a single file in sandboxes by providing the content to upload and the path to the file to upload it to.
```python
# Upload a single file
with open("local_file.txt", "rb") as f:
content = f.read()
sandbox.fs.upload_file(content, "remote_file.txt")
```
```typescript
// Upload a single file
const fileContent = Buffer.from('Hello, World!')
await sandbox.fs.uploadFile(fileContent, 'data.txt')
```
```ruby
# Upload a text file from string content
content = "Hello, World!"
sandbox.fs.upload_file(content, "tmp/hello.txt")
# Upload a local file
sandbox.fs.upload_file("local_file.txt", "tmp/file.txt")
# Upload binary data
data = { key: "value" }.to_json
sandbox.fs.upload_file(data, "tmp/config.json")
```
```go
// Upload from a local file path
err := sandbox.FileSystem.UploadFile(ctx, "local_file.txt", "remote_file.txt")
if err != nil {
log.Fatal(err)
}
// Or upload from byte content
content := []byte("Hello, World!")
err = sandbox.FileSystem.UploadFile(ctx, content, "hello.txt")
if err != nil {
log.Fatal(err)
}
```
```java
import java.nio.charset.StandardCharsets;
byte[] fileContent = "Hello, World!".getBytes(StandardCharsets.UTF_8);
sandbox.fs.uploadFile(fileContent, "data.txt");
```
```bash
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/upload?path=' \
--request POST \
--header 'Content-Type: multipart/form-data' \
--form 'file='
```
#### Upload multiple files
Daytona provides methods to upload multiple files in sandboxes by providing the content to upload and their destination paths.
```python
# Upload multiple files at once
files_to_upload = []
with open("file1.txt", "rb") as f1:
files_to_upload.append(FileUpload(
source=f1.read(),
destination="data/file1.txt",
))
with open("file2.txt", "rb") as f2:
files_to_upload.append(FileUpload(
source=f2.read(),
destination="data/file2.txt",
))
with open("settings.json", "rb") as f3:
files_to_upload.append(FileUpload(
source=f3.read(),
destination="config/settings.json",
))
sandbox.fs.upload_files(files_to_upload)
```
```typescript
// Upload multiple files at once
const files = [
{
source: Buffer.from('Content of file 1'),
destination: 'data/file1.txt',
},
{
source: Buffer.from('Content of file 2'),
destination: 'data/file2.txt',
},
{
source: Buffer.from('{"key": "value"}'),
destination: 'config/settings.json',
},
]
await sandbox.fs.uploadFiles(files)
```
```ruby
# Upload multiple files
files = [
FileUpload.new("Content of file 1", "/tmp/file1.txt"),
FileUpload.new("workspace/data/file2.txt", "/tmp/file2.txt"),
FileUpload.new('{"key": "value"}', "/tmp/config.json")
]
sandbox.fs.upload_files(files)
```
```go
// Upload multiple files by calling UploadFile for each
filesToUpload := []struct {
source string
destination string
}{
{"file1.txt", "data/file1.txt"},
{"file2.txt", "data/file2.txt"},
{"settings.json", "config/settings.json"},
}
for _, f := range filesToUpload {
err := sandbox.FileSystem.UploadFile(ctx, f.source, f.destination)
if err != nil {
log.Fatal(err)
}
}
```
```bash
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/bulk-upload' \
--request POST
```
### Download files
Daytona provides methods to download files from sandboxes.
#### Download a single file
Daytona provides methods to download a single file from sandboxes by providing the path to the file to download.
```python
from daytona import DaytonaNotFoundError
try:
content = sandbox.fs.download_file("file1.txt")
except DaytonaNotFoundError as error:
print(f"Missing file: {error}")
else:
with open("local_file.txt", "wb") as f:
f.write(content)
print(content.decode("utf-8"))
```
```typescript
import { DaytonaNotFoundError } from '@daytona/sdk'
try {
const downloadedFile = await sandbox.fs.downloadFile('file1.txt')
console.log('File content:', downloadedFile.toString())
} catch (error) {
if (error instanceof DaytonaNotFoundError) {
console.error(`Missing file: ${error.message}`)
} else {
throw error
}
}
```
```ruby
# Download and get file content
content = sandbox.fs.download_file("workspace/data/file.txt")
puts content
# Download and save a file locally
sandbox.fs.download_file("workspace/data/file.txt", "local_copy.txt")
size_mb = File.size("local_copy.txt") / 1024.0 / 1024.0
puts "Size of the downloaded file: #{size_mb} MB"
```
```go
// Download and get contents in memory
content, err := sandbox.FileSystem.DownloadFile(ctx, "file1.txt", nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(content))
// Download and save to a local file
localPath := "local_file.txt"
content, err = sandbox.FileSystem.DownloadFile(ctx, "file1.txt", &localPath)
if err != nil {
log.Fatal(err)
}
```
```java
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
byte[] content = sandbox.fs.downloadFile("file1.txt");
System.out.println(new String(content, StandardCharsets.UTF_8));
Files.write(Path.of("local_file.txt"), content);
```
```bash
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/download?path='
```
In the Python and TypeScript SDKs, `download_file` and `downloadFile` raise typed Daytona exceptions when the daemon returns structured per-file error metadata. Missing files map to not-found errors, invalid paths such as directories map to validation errors, and permission failures map to authorization errors.
#### Download multiple files
Daytona provides methods to download multiple files from sandboxes by providing the paths to the files to download.
```python
# Download multiple files at once
files_to_download = [
FileDownloadRequest(source="data/file1.txt"), # No destination - download to memory
FileDownloadRequest(source="data/file2.txt", destination="local_file2.txt"), # Download to local file
]
results = sandbox.fs.download_files(files_to_download)
for result in results:
if result.error:
print(f"Error downloading {result.source}: {result.error}")
if result.error_details:
print(
f" status={result.error_details.status_code} "
f"code={result.error_details.error_code}"
)
elif result.result:
print(f"Downloaded {result.source} to {result.result}")
```
```typescript
// Download multiple files at once
const files = [
{ source: 'data/file1.txt' }, // No destination - download to memory
{ source: 'data/file2.txt', destination: 'local_file2.txt' }, // Download to local file
]
const results = await sandbox.fs.downloadFiles(files)
results.forEach(result => {
if (result.error) {
console.error(`Error downloading ${result.source}: ${result.error}`)
if (result.errorDetails) {
console.error(
` status=${result.errorDetails.statusCode} code=${result.errorDetails.errorCode}`
)
}
} else if (result.result) {
console.log(`Downloaded ${result.source} to ${result.result}`)
}
})
```
```ruby
# Download multiple files by calling download_file for each
files_to_download = [
{ remote: "data/file1.txt", local: nil }, # Download to memory
{ remote: "data/file2.txt", local: "local_file2.txt" } # Download to local file
]
files_to_download.each do |f|
if f[:local]
sandbox.fs.download_file(f[:remote], f[:local])
puts "Downloaded #{f[:remote]} to #{f[:local]}"
else
content = sandbox.fs.download_file(f[:remote])
puts "Downloaded #{f[:remote]} to memory (#{content.size} bytes)"
end
end
```
```go
// Download multiple files by calling DownloadFile for each
filesToDownload := []struct {
remotePath string
localPath *string
}{
{"data/file1.txt", nil}, // Download to memory
{"data/file2.txt", ptrString("local_file2.txt")}, // Download to local file
}
for _, f := range filesToDownload {
content, err := sandbox.FileSystem.DownloadFile(ctx, f.remotePath, f.localPath)
if err != nil {
fmt.Printf("Error downloading %s: %v\n", f.remotePath, err)
continue
}
if f.localPath == nil {
fmt.Printf("Downloaded %s to memory (%d bytes)\n", f.remotePath, len(content))
} else {
fmt.Printf("Downloaded %s to %s\n", f.remotePath, *f.localPath)
}
}
```
```bash
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/bulk-download' \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"paths": [
""
]
}'
```
Bulk downloads keep the existing `error` string for compatibility and now also include structured metadata on each failed item:
- Python: `result.error_details.message`, `result.error_details.status_code`, `result.error_details.error_code`
- TypeScript: `result.errorDetails.message`, `result.errorDetails.statusCode`, `result.errorDetails.errorCode`
The toolbox bulk-download API returns successful files as multipart `file` parts and per-file failures as multipart `error` parts with JSON payloads containing `message`, `statusCode`, and `code`.
### Delete files
Daytona provides methods to delete files or directories from sandboxes by providing the path to the file or directory to delete.
```python
sandbox.fs.delete_file("workspace/file.txt")
```
```typescript
await sandbox.fs.deleteFile('workspace/file.txt')
```
```ruby
# Delete a file
sandbox.fs.delete_file("workspace/data/old_file.txt")
# Delete a directory recursively
sandbox.fs.delete_file("workspace/old_dir", recursive: true)
```
```go
// Delete a file
err := sandbox.FileSystem.DeleteFile(ctx, "workspace/file.txt", false)
if err != nil {
log.Fatal(err)
}
// Delete a directory recursively
err = sandbox.FileSystem.DeleteFile(ctx, "workspace/old_dir", true)
if err != nil {
log.Fatal(err)
}
```
```java
sandbox.fs.deleteFile("workspace/file.txt");
```
```bash
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files?path=' \
--request DELETE
```
## Advanced operations
Daytona provides advanced file system operations such as file permissions, search and replace, and move files.
### File permissions
Daytona provides methods to set file permissions, ownership, and group for a file or directory by providing the path to the file or directory and the permissions to set.
```python
# Set file permissions
sandbox.fs.set_file_permissions("workspace/file.txt", "644")
# Get file permissions
file_info = sandbox.fs.get_file_info("workspace/file.txt")
print(f"Permissions: {file_info.permissions}")
```
```typescript
// Set file permissions
await sandbox.fs.setFilePermissions('workspace/file.txt', { mode: '644' })
// Get file permissions
const fileInfo = await sandbox.fs.getFileDetails('workspace/file.txt')
console.log(`Permissions: ${fileInfo.permissions}`)
```
```ruby
# Make a file executable
sandbox.fs.set_file_permissions(
path: "workspace/scripts/run.sh",
mode: "755" # rwxr-xr-x
)
# Change file owner
sandbox.fs.set_file_permissions(
path: "workspace/data/file.txt",
owner: "daytona",
group: "daytona"
)
```
```go
// Set file permissions
err := sandbox.FileSystem.SetFilePermissions(ctx, "workspace/file.txt",
options.WithPermissionMode("644"),
)
if err != nil {
log.Fatal(err)
}
// Set owner and group
err = sandbox.FileSystem.SetFilePermissions(ctx, "workspace/file.txt",
options.WithOwner("daytona"),
options.WithGroup("daytona"),
)
if err != nil {
log.Fatal(err)
}
// Get file info to check permissions
fileInfo, err := sandbox.FileSystem.GetFileInfo(ctx, "workspace/file.txt")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Mode: %s\n", fileInfo.Mode)
```
```bash
curl 'https://proxy.app.daytona.io/toolbox/{sandboxId}/files/permissions?path=' \
--request POST
```
### Find and replace text in files
Daytona provides methods to find and replace text in files by providing the path to the directory to search in and the pattern to search for.
```python
# Search for text in files by providing the path to the directory to search in and the pattern to search for
results = sandbox.fs.find_files(
path="workspace/src",
pattern="text-of-interest"
)
for match in results:
print(f"Absolute file path: {match.file}")
print(f"Line number: {match.line}")
print(f"Line content: {match.content}")
print("\n")
# Replace text in files
sandbox.fs.replace_in_files(
files=["workspace/file1.txt", "workspace/file2.txt"],
pattern="old_text",
new_value="new_text"
)
```
```typescript
// Search for text in files; if a folder is specified, the search is recursive
const results = await sandbox.fs.findFiles({
path="workspace/src",
pattern: "text-of-interest"
})
results.forEach(match => {
console.log('Absolute file path:', match.file)
console.log('Line number:', match.line)
console.log('Line content:', match.content)
})
// Replace text in files
await sandbox.fs.replaceInFiles(
["workspace/file1.txt", "workspace/file2.txt"],
"old_text",
"new_text"
)
```
```ruby
# Search for TODOs in Ruby files
matches = sandbox.fs.find_files("workspace/src", "TODO:")
matches.each do |match|
puts "#{match.file}:#{match.line}: #{match.content.strip}"
end
# Replace in specific files
results = sandbox.fs.replace_in_files(
files: ["workspace/src/file1.rb", "workspace/src/file2.rb"],
pattern: "old_function",
new_value: "new_function"
)
# Print results
results.each do |result|
if result.success
puts "#{result.file}: #{result.success}"
else
puts "#{result.file}: #{result.error}"
end
end
```
```go
// Search for text in files
result, err := sandbox.FileSystem.FindFiles(ctx, "workspace/src", "text-of-interest")
if err != nil {
log.Fatal(err)
}
matches := result.([]map[string]any)
for _, match := range matches {
fmt.Printf("Absolute file path: %s\n", match["file"])
fmt.Printf("Line number: %v\n", match["line"])
fmt.Printf("Line content: %s\n\n", match["content"])
}
// Replace text in files
_, err = sandbox.FileSystem.ReplaceInFiles(ctx,
[]string{"workspace/file1.txt", "workspace/file2.txt"},
"old_text",
"new_text",
)
if err != nil {
log.Fatal(err)
}
```
```java
import java.util.Arrays;
import java.util.List;
import java.util.Map;
List