DaytonaDocs

Using Volumes

Create volumes and attach them to sandboxes.

This page covers the mechanics of working with volumes: creating and managing them, attaching them to sandboxes at create time, and sharing data between sandboxes. Volumes are managed through daytona.volume in the SDKs and the /volumes routes in the REST API; attachment happens as part of sandbox creation.

Creating a volume

Volumes are created with a name and an optional size, and are ready to attach immediately. Names must be 1–128 characters of [A-Za-z0-9._-] and unique within the project; size_gb defaults to 100 and can be 1–10,000 GiB. The only volume type in v1 is blockmount, which is also the default.

The most convenient SDK call is get-or-create, which fetches the volume by name and creates it if missing — idempotent setup code:

Python
from rlp import Daytona
 
daytona = Daytona()
 
volume = daytona.volume.get("training-data", create=True)
 
# Or create explicitly with a size:
big = daytona.volume.create("model-cache", size_gb=500)
TypeScript
import { Daytona } from '@rlp/sdk'
 
const daytona = new Daytona()
 
const volume = await daytona.volume.get('training-data', true)
 
// Or create explicitly with a size:
const big = await daytona.volume.create('model-cache', 500)
cURL
curl -X POST https://api.rl.trydaytona.com/volumes \
  -H "Authorization: Bearer $RLP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "training-data", "type": "blockmount", "size_gb": 100}'

Attaching volumes to a sandbox

Volumes are attached at sandbox create time, each at an absolute mount_path inside the sandbox. Optionally, subpath mounts a subdirectory of the volume instead of its root — useful for giving a sandbox just one dataset out of a larger volume.

Python
from rlp import CreateSandboxFromImageParams, Daytona, VolumeMount
 
daytona = Daytona()
volume = daytona.volume.get("training-data", create=True)
 
sandbox = daytona.create(CreateSandboxFromImageParams(
    image="python:3.12-slim",
    volumes=[VolumeMount(volume_id=volume["id"], mount_path="/data", subpath="opt")],
))
TypeScript
const volume = await daytona.volume.get('training-data', true)
 
const sandbox = await daytona.create({
  image: 'python:3.12-slim',
  volumes: [{ volumeId: volume.id, mountPath: '/data', subpath: 'opt' }],
})
cURL
curl -X POST https://api.rl.trydaytona.com/vms \
  -H "Authorization: Bearer $RLP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "python:3.12-slim",
    "volumes": [
      { "volume": "training-data", "mount_path": "/data", "subpath": "opt" }
    ]
  }'

Note: the SDK mount field is volume_id / volumeId (it accepts a volume id or name — the platform resolves it), while the raw REST field is named "volume".

Attachment rules the API enforces:

  • At most 8 volumes per sandbox.
  • mount_path must be absolute, contain no .., and be unique among the sandbox's mounts.
  • Reserved paths are rejected: /, /proc, /sys, /dev, /overlay, /mnt/scratch.
  • The volume must be in state ready (a deleted or errored volume returns 409 Conflict).

Remember that a sandbox created with volumes cold-boots, and cannot pause or fork in v1 — see the overview for the full interaction list.

Sharing data between sandboxes

Because commits happen in the background (~5-second cadence), data written by one sandbox becomes visible to newly attaching sandboxes shortly after it's written. A typical producer/consumer flow:

Python
# Producer sandbox: generate results onto the volume.
producer.process.exec("python generate.py --out /data/results")
 
# Give the background commit a moment to pick up the writes,
# then attach the same volume elsewhere.
consumer = daytona.create(CreateSandboxFromImageParams(
    image="python:3.12-slim",
    volumes=[VolumeMount(volume_id=volume["id"], mount_path="/data")],
))
consumer.process.exec("ls /data/results")

Concurrent writers to the same volume are merged per-file, last-change-wins; conflicts are recorded on the volume (visible on GET /volumes/:id) but never block anyone. Structure concurrent workloads so each writer owns its own files.

Managing volumes

List, inspect, and delete volumes by id or name:

Python
volumes = daytona.volume.list()                      # active volumes
everything = daytona.volume.list(include_deleted=True)
 
vol = daytona.volume.get("training-data")            # detail, incl. usage info
daytona.volume.delete(vol)
TypeScript
const volumes = await daytona.volume.list()
const everything = await daytona.volume.list(true)   // include deleted
 
const vol = await daytona.volume.get('training-data')
await daytona.volume.delete(vol)
cURL
# List (add ?include_deleted=true to see retired volumes)
curl https://api.rl.trydaytona.com/volumes \
  -H "Authorization: Bearer $RLP_API_KEY"
 
# Get one by id or name
curl https://api.rl.trydaytona.com/volumes/training-data \
  -H "Authorization: Bearer $RLP_API_KEY"
 
# Delete
curl -X DELETE https://api.rl.trydaytona.com/volumes/training-data \
  -H "Authorization: Bearer $RLP_API_KEY"

Deletion is refused with 409 Conflict while any live sandbox has the volume attached — delete or stop those sandboxes first. After deletion the volume's name is freed for reuse; deleted volumes remain visible via include_deleted for auditing.

See also

  • Volumes Overview — the model: local-first performance, merge semantics, durability.
  • Object stores — mounting existing S3-compatible buckets instead.

On this page