Sign Up

Volumes

Shared file access across sandboxes.

Volumes are named, project-scoped storage that persists independently of any sandbox. Sandboxes come and go; a volume persists, and any number of sandboxes can attach it, one after another or several at the same time. Use volumes for datasets, model weights, package caches, build artifacts, and per-tenant data.

Volumes make a sandbox's data durable and shared; a persistent sandbox makes the whole machine durable, and it belongs to exactly one sandbox.

ConceptDescription
VolumeA named, project-scoped directory tree that exists independently of any sandbox. Created once with a type, attached to any number of sandboxes.
MountA volume attached to one sandbox at an absolute path, optionally scoped to a subpath and, for bucket volumes, mounted read-only. A sandbox can have up to 8 volume mounts.
CommitDisk volumes only. The background sync from a sandbox's local copy back to the volume. Once committed, data survives sandbox teardown and host failure.

Use cases

Volumes are used when data must outlive a sandbox or be shared between sandboxes.

  • Datasets and model weights: large files are stored once and attached to every sandbox that reads them, instead of downloaded at each boot.
  • Package and build caches: dependency caches and build outputs on a volume carry over from one ephemeral sandbox to the next.
  • Handoff between sandboxes: a producer sandbox writes results to a disk volume, and a consumer sandbox attaches the volume after the commit.
  • Shared reference data: a bucket volume mounted read-only gives many sandboxes the same data, and none of them can change it.
  • Per-tenant data: each tenant's data lives under its own subpath, and a sandbox mounts only the subpath of its tenant.
  • Data in your own bucket: a volume on a bucket you own exposes its existing objects to sandboxes, and the bucket stays under your control.
  • Live sharing between running sandboxes: sandboxes that run at the same time mount one bucket volume. A file that one sandbox closes is visible to the others within seconds.
  • Results from ephemeral sandboxes: an ephemeral or spot sandbox writes its results to a volume. The results survive the deletion or the reclaim of the sandbox.

Create volumes

Daytona provides two volume types:

  • Disk volume gives each sandbox a fast local copy, merged back on commit.
  • Bucket volume mounts an S3 bucket live inside the sandbox, so running sandboxes see each other's files within seconds and you can point a volume at a bucket you already own.
  1. Go to Daytona Volumes ↗
  2. Click Create Volume
  3. Enter a Name and select a Type
  4. For a disk volume, set Size (GiB)
  5. For a bucket volume, leave the fields empty for a platform-owned bucket, or enter your Bucket name, Region, Endpoint, Prefix, and the Access key and Secret key secret names
  6. Click Create

Disk volumes

A disk volume (the default type) is a private local disk per sandbox. The sandbox does not perform remote I/O on every read and write. It works on a fast, private local copy of the volume at local-disk speed, and changes commit back to the volume in the background. Committed data survives sandbox deletion and becomes visible to other sandboxes that attach the same volume after the commit landed. A running sandbox never sees another sandbox's writes.

from daytona import Daytona

daytona = Daytona()
volume = daytona.v2.volume.create(name="training-data")
ParameterTypeDescription
namestrSets the volume name. The name is 1 to 128 characters of letters, digits, ., _, and -. It must not start with . or with a reserved prefix: snap-, build-, refs, by-digest, or prj-. Names are unique within a project. This parameter is keyword-only and required.
size_gbintSets the disk volume size in GiB, from 1 to 10000. This parameter is keyword-only. The default is 100.

Bucket volumes

A bucket volume mounts an S3 bucket live inside the sandbox. Every mounted sandbox reads and writes the same objects, and a file one sandbox closes becomes visible to the others within seconds. The bucket has one of two origins:

  • Platform-owned: omit the bucket block, and Daytona creates and owns the bucket.
  • External: pass a bucket block naming a bucket you own, with credentials as the names of two project secrets; their values never enter the API.
daytona volume create llama-weights --type bucket

To point the volume at a bucket you own, add a bucket block. See bring your own bucket.

Bring your own bucket

Create a bucket volume on a bucket you own. Daytona stores the bucket location and the names of two project secrets that hold the access key and the secret key. It does not contact the bucket when it creates the volume, and it never creates or deletes your bucket.

Both secrets must exist in the project before you create the volume.

daytona volume create corp-data \
  --type bucket \
  --bucket corp-data \
  --region us-east-1 \
  --access-key-secret AWS_KEY \
  --secret-key-secret AWS_SECRET
FieldDescription
nameBucket name, 3 to 63 characters of [a-z0-9.-]. Required.
credentialsaccess_key_secret and secret_key_secret, the names of two project secrets. Required.
regionBucket region. Defaults to the deployment's default bucket region.
endpointNon-AWS S3 endpoint as an http(s):// URL, for R2, MinIO, and similar. Absent means AWS.
prefixKey prefix the volume is scoped to. A trailing / is added if missing. Empty means the whole bucket.
path_styleUse path-style addressing. Defaults to false.

Mount volumes

Mount volumes when creating a sandbox.

from daytona import Daytona
from daytona.v2 import VolumeMount

daytona = Daytona()
volume = daytona.v2.volume.get("my-awesome-volume", create=True)

sandbox = daytona.v2.create(
    volumes=[VolumeMount(volume_id=volume.id, mount_path="/data")],
)
ParameterTypeDescription
createboolCreates a disk volume with the default size if no volume has that name. If it is False, a missing volume raises NotFoundError. The default is False.

Mount with subpath

Mount part of a volume. The sandbox sees only that subtree: a disk volume subdirectory, or a key prefix under a bucket volume's prefix. Several sandboxes can mount different subtrees.

from daytona import Daytona
from daytona.v2 import VolumeMount

daytona = Daytona()
volume = daytona.v2.volume.get("my-awesome-volume", create=True)

# Each sandbox mounts only its tenant's slice of the shared volume
sandbox = daytona.v2.create(
    volumes=[VolumeMount(
        volume_id=volume.id,
        mount_path="/data",
        subpath="tenants/tenant-a",
    )],
)

Mount read-only

Mount a bucket volume without write access. Read-only mounts apply to bucket volumes only; a read-only mount of a disk volume fails the sandbox create.

from daytona import Daytona
from daytona.v2 import VolumeMount

daytona = Daytona()
volume = daytona.v2.volume.get("llama-weights")

sandbox = daytona.v2.create(
    volumes=[VolumeMount(volume_id=volume.id, mount_path="/models", read_only=True)],
)

Share data between sandboxes

Share through a bucket volume

Share files between sandboxes that run at the same time. Both sandboxes mount the same bucket volume, and a file the producer closes becomes visible to the consumer within the directory listing cache window of a few seconds.

import time

from daytona import Daytona
from daytona.v2 import VolumeMount

daytona = Daytona()

# Created with `POST /volumes {"name": "rollouts", "type": "bucket"}`
volume = daytona.v2.volume.get("rollouts")

producer = daytona.v2.create(
    volumes=[VolumeMount(volume_id=volume.id, mount_path="/out")],
)
consumer = daytona.v2.create(
    volumes=[VolumeMount(volume_id=volume.id, mount_path="/in", read_only=True)],
)

producer.process.run("mkdir -p /out/results && echo done > /out/results/done.txt")

# Closed files reach the consumer within the listing cache window, so poll
for _ in range(30):
    response = consumer.process.run("ls /in/results")
    if response.exit_code == 0:
        break
    time.sleep(1)
print(response.output)

Share through a disk volume

Hand data from one sandbox to the next. A running sandbox never sees another sandbox's writes, so the consumer attaches the disk volume after the producer's changes are committed. Stopping or deleting the producer runs a final commit; the consumer waits until the volume reports a new commit before it attaches.

import time

from daytona import Daytona
from daytona.v2 import VolumeMount

daytona = Daytona()
volume = daytona.v2.volume.get("results", create=True)

# Producer sandbox writes results onto the volume
producer = daytona.v2.create(
    volumes=[VolumeMount(volume_id=volume.id, mount_path="/data")],
)

# Record the commit pointer before writing, so any later commit changes it
before = daytona.v2.volume.get(volume.id).last_manifest_id
producer.process.run("mkdir -p /data/results && echo done > /data/results/done.txt && sync")

# Deleting the writer triggers its final commit
producer.delete()

# Wait for the commit to land before the next sandbox attaches
for _ in range(30):
    if daytona.v2.volume.get(volume.id).last_manifest_id != before:
        break
    time.sleep(1)

consumer = daytona.v2.create(
    volumes=[VolumeMount(volume_id=volume.id, mount_path="/data")],
)
response = consumer.process.run("ls /data/results")
print(response.output)

Operations

List volumes

  1. Go to Daytona Dashboard ↗
  2. Filter the list by state or bucket origin
from daytona import Daytona

daytona = Daytona()

page = daytona.v2.volume.list()
for volume in page.items:
    print(volume.name, volume.type, volume.state)
Iterate over all volumes
from daytona import Daytona

daytona = Daytona()

for volume in daytona.v2.volume.iter():
    print(volume.name, volume.type, volume.state)

Get a volume

Get a volume by ID or name. A reference that matches one volume's ID and another volume's name is ambiguous; resolve it as an ID only to avoid this. The detail response adds commit information for disk volumes, read from the volume store on a best-effort basis.

FieldDescription
size_gbCapacity of the per-sandbox local copy. Disk volumes only; null for a bucket volume.
optionsPer-type options: {} for a disk volume, {"bucket": {...}} for a bucket volume. Credentials appear as secret names, never values.
used_bytesLogical size of the last committed tree, in bytes. Disk volumes only; absent until the first commit.
used_bytes_atWhen used_bytes was observed.
last_manifest_idPointer to the latest commit. Disk volumes only; read best-effort from the store, detail routes only.
conflictsConcurrent-write conflicts recorded on the latest commit. Disk volumes only.
last_used_atWhen a sandbox last attached the volume.
error_reasonWhy the volume is in state error, if it is.
from daytona import Daytona

daytona = Daytona()

volume = daytona.v2.volume.get("my-awesome-volume")
ParameterTypeDescription
id_onlyboolResolves the argument as an ID only, if True. The default is False.

Delete volumes

Delete a volume. Daytona refuses to delete a volume a sandbox still attaches: delete the sandbox and wait for its teardown first. Deleting a platform-owned bucket volume also deletes its bucket and all its objects; deleting a volume on your own bucket leaves it untouched.

  1. Go to Daytona Dashboard ↗
  2. Click the ⋮ menu of the volume and click Delete
  3. Click Delete Volume to confirm
from daytona import Daytona

daytona = Daytona()

daytona.v2.volume.delete("my-awesome-volume")