Persistent Volumes
Use Tenki Sandbox persistent volumes for durable build caches, package caches, large repositories, and datasets that survive session termination.
Persistent volumes are workspace-scoped block storage that survives the session it was used in. Volumes are the right tool for durable data: package caches, build caches, large repos, datasets, anything you want available across many sessions.
Volume lifecycle
Pick your tool, and the selection syncs across the rest of the page.
Create
tenki sandbox volume create --name npm-cache --size 20GBThe CLI requires explicit units:
10GB,10GiB,500MB: accepted- bare
1024: rejected
import { GiB } from "@tenkicloud/sandbox";
const volume = await sandbox.createVolume({
name: "data-vol",
sizeBytes: 10 * GiB,
});
await sandbox.waitVolumeReady(volume.id);volume, err := client.CreateVolume(
ctx,
tenkisandbox.WithVolumeName("npm-cache"),
tenkisandbox.WithVolumeSize(20*tenkisandbox.GB),
)
volume, err = client.WaitVolumeReady(ctx, volume.ID, 3*time.Minute)Useful size constants: tenkisandbox.KB, MB, GB (and KiB, MiB, GiB for binary units).
from tenki import Client, GiB
client = Client()
volume = client.volumes.create(
name="npm-cache",
size_bytes=20 * GiB,
)
volume = client.volumes.wait_ready(volume.id, timeout=180)Size constants are exported at the top level: KB, MB, GB (and KiB, MiB, GiB for binary units).
Volumes can be between 1 MiB and 100 GiB.
List, get, resize, delete
const volumes = await sandbox.listVolumes();
const volume = await sandbox.getVolume(volumeId);
await sandbox.resizeVolume(volumeId, 40 * GiB);
await sandbox.deleteVolume(volumeId);volumes = client.volumes.list()
volume = client.volumes.get(volume_id)
volume = client.volumes.resize(volume_id, 40 * GB)
client.volumes.delete(volume_id)volumes, err := client.ListVolumes(ctx)
volume, err := client.GetVolume(ctx, volumeID)
volume, err = client.ResizeVolume(ctx, volumeID, 40*tenkisandbox.GB)
err = client.DeleteVolume(ctx, volumeID)tenki sandbox volume list
tenki sandbox volume list --json
tenki sandbox volume get <volume-id>
tenki sandbox volume resize <volume-id> --size 40GB
tenki sandbox volume delete <volume-id>Attach to sessions
Attach at create time
tenki sandbox create \
--volume <volume-id>:/workspace/cache \
--volume <other-id>:/workspace/reference:roThe optional :ro suffix mounts the volume read-only.
const session = await sandbox.createAndWait({
volumes: [
{ volumeId: cacheVolumeId, mountPath: "/workspace/cache" },
{ volumeId: refVolumeId, mountPath: "/workspace/reference", readOnly: true },
],
});session, err := client.Create(
ctx,
tenkisandbox.WithVolume(cacheVolumeID, "/workspace/cache"),
tenkisandbox.WithVolume(refVolumeID, "/workspace/reference", tenkisandbox.WithReadOnly()),
)sb = Sandbox.create(
volumes=[
{"volume_id": cache_volume_id, "mount_path": "/workspace/cache"},
{"volume_id": ref_volume_id, "mount_path": "/workspace/reference", "read_only": True},
],
)Attach to a running session
tenki sandbox volume attach <session-id> <volume-id> --mount /workspace/cache
tenki sandbox volume attach <session-id> <volume-id> --mount /workspace/reference --readonly
tenki sandbox volume detach <session-id> <volume-id>await session.attachVolume(volume.id, "/mnt/data");
await session.detachVolume(volume.id);err := session.AttachVolume(ctx, volumeID, "/workspace/cache")
err = session.AttachVolume(ctx, refVolumeID, "/workspace/reference", tenkisandbox.WithReadOnly())
err = session.DetachVolume(ctx, volumeID)sb.attach_volume(volume_id, "/workspace/cache")
sb.attach_volume(ref_volume_id, "/workspace/reference", read_only=True)
sb.detach_volume(volume_id)Workspace scope
Volumes belong to the workspace bound to your API key. Create and list calls infer that workspace, so do not pass a workspace ID.
Snapshots and volumes
Snapshots do not auto-reattach a session's volumes. A restored session is a normal session, so reattach any volume explicitly, at create time or with volume attach. See Snapshots for the restore workflow.
Keep durable data in volumes, not snapshots: baking a cache or dataset into a snapshot bloats it and ties that data to one VM's history.
Errors to handle
Volume calls can fail with a not-found, in-use, or quota error. The one worth handling is in-use: the volume is still attached to another session, so detach it from that session or wait, then retry. Every SDK surfaces these as typed errors or exceptions; see the SDK reference for the names in each language.