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.
- 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 ↗ or programmatically using the Daytona Python, TypeScript, Ruby, Go SDKs, CLI, or API.
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
- avoids mounting a separate volume for every user or sandbox
- continues to provide strong isolation at the mount boundary
- Navigate to Daytona Volumes ↗
- Click the Create Volume button
- Enter the volume name
daytona = Daytona()volume = daytona.volume.create("my-awesome-volume")const daytona = new Daytona()const volume = await daytona.volume.create('my-awesome-volume')daytona = Daytona::Daytona.newvolume = daytona.volume.create("my-awesome-volume")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)}daytona volume create my-awesome-volumecurl 'https://app.daytona.io/api/volumes' \ --request POST \ --header 'Authorization: Bearer <API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "name": "my-awesome-volume"}'For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
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
from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount
daytona = Daytona()
# Create a new volume or get an existing onevolume = daytona.volume.get("my-volume", create=True)
mount_dir = "/home/daytona/volume"
# Recommended for per-user / per-tenant data: one volume, unique subpath per sandboxparams = 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 treeparams_full = CreateSandboxFromSnapshotParams( language="python", volumes=[VolumeMount(volume_id=volume.id, mount_path=mount_dir)],)sandbox_shared = daytona.create(params_full)import { Daytona } from '@daytona/sdk'import path from 'path'
const daytona = new Daytona()const volume = await daytona.volume.get('my-volume', true)const mountDir = '/home/daytona/volume'
// Recommended for per-user / per-tenant data: one volume, unique subpath per sandboxconst 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 treeconst sandboxShared = await daytona.create({ language: 'typescript', volumes: [{ volumeId: volume.id, mountPath: mountDir }],})require 'daytona'
daytona = Daytona::Daytona.newvolume = daytona.volume.get('my-volume', create: true)mount_dir = '/home/daytona/volume'
# Recommended for per-user / per-tenant data: one volume, unique subpath per sandboxparams = 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 treeparams_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)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 onevolume, err := client.Volume.Get(context.Background(), "my-volume")if err != nil { // If volume doesn't exist, create it volume, err = client.Volume.Create(context.Background(), "my-volume") if err != nil { log.Fatal(err) }}
mountDir := "/home/daytona/volume"
// Recommended for per-user / per-tenant data: one volume, unique subpath per sandboxsubpath := "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)}daytona volume create my-volumedaytona create --volume my-volume:/home/daytona/volumeThe --volume flag accepts VOLUME_NAME:MOUNT_PATH only. For a subpath mount, use a SDK or the API and set subpath on the volume entry.
curl 'https://app.daytona.io/api/sandbox' \ --request POST \ --header 'Authorization: Bearer <API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "volumes": [ { "volumeId": "<VOLUME_ID>", "mountPath": "/home/daytona/volume", "subpath": "users/alice" } ]}'Omit subpath to mount the full volume at mountPath.
For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
CreateSandboxFromSnapshotParams (Python SDK)
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:
# Write to a file in the mounted volume using the Sandbox file system APIsandbox.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 removedsandbox.delete()// Write to a file in the mounted volume using the Sandbox file system APIawait 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 removedawait daytona.delete(sandbox)# Write to a file in the mounted volume using the Sandbox file system APIsandbox.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 removeddaytona.delete(sandbox)import ( "context" "log")
// Write to a file in the mounted volumeerr := 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 removederr = sandbox.Delete(context.Background())if err != nil { log.Fatal(err)}For more information, see the Python SDK, TypeScript SDK, Ruby SDK, and Go SDK references.
Get a volume by name
Daytona provides an option to get a volume by its name.
daytona = Daytona()volume = daytona.volume.get("my-awesome-volume", create=True)print(f"{volume.name} ({volume.id})")const daytona = new Daytona()const volume = await daytona.volume.get('my-awesome-volume', true)console.log(`Volume ${volume.name} is in state ${volume.state}`)daytona = Daytona::Daytona.newvolume = daytona.volume.get('my-awesome-volume', create: true)puts "#{volume.name} (#{volume.id})"import ( "context" "fmt" "log"
"github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona")
client, err := daytona.NewClient()if err != nil { log.Fatal(err)}volume, err := client.Volume.Get(context.Background(), "my-awesome-volume")if err != nil { log.Fatal(err)}fmt.Printf("Volume %s is in state %s\n", volume.Name, volume.State)daytona volume get my-awesome-volumecurl 'https://app.daytona.io/api/volumes/by-name/my-awesome-volume' \ --header 'Authorization: Bearer <API_KEY>'For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
List volumes
Daytona provides an option to list all volumes.
daytona = Daytona()volumes = daytona.volume.list()for volume in volumes: print(f"{volume.name} ({volume.id})")const daytona = new Daytona()const volumes = await daytona.volume.list()console.log(`Found ${volumes.length} volumes`)volumes.forEach(vol => console.log(`${vol.name} (${vol.id})`))daytona = Daytona::Daytona.newvolumes = daytona.volume.listvolumes.each do |volume| puts "#{volume.name} (#{volume.id})"endimport ( "context" "fmt" "log"
"github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona")
client, err := daytona.NewClient()if err != nil { log.Fatal(err)}volumes, err := client.Volume.List(context.Background())if err != nil { log.Fatal(err)}fmt.Printf("Found %d volumes\n", len(volumes))for _, vol := range volumes { fmt.Printf("%s (%s)\n", vol.Name, vol.ID)}daytona volume listcurl 'https://app.daytona.io/api/volumes' \ --header 'Authorization: Bearer <API_KEY>'For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
Delete volumes
Daytona provides an option to delete a volume. Deleted volumes cannot be recovered.
The following snippet demonstrate how to delete a volume:
volume = daytona.volume.get("my-volume", create=True)daytona.volume.delete(volume)const volume = await daytona.volume.get('my-volume', true)await daytona.volume.delete(volume)volume = daytona.volume.get('my-volume', create: true)daytona.volume.delete(volume)import ( "context" "log"
"github.com/daytonaio/daytona/libs/sdk-go/pkg/daytona")
client, err := daytona.NewClient()if err != nil { log.Fatal(err)}volume, err := client.Volume.Get(context.Background(), "my-volume")if err != nil { log.Fatal(err)}err = client.Volume.Delete(context.Background(), volume)if err != nil { log.Fatal(err)}daytona volume delete <VOLUME_ID>curl 'https://app.daytona.io/api/volumes/<VOLUME_ID>' \ --request DELETE \ --header 'Authorization: Bearer <API_KEY>'For more information, see the Python SDK, TypeScript SDK, Ruby SDK, Go SDK, and API references:
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 Dashboard ↗.