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
Create
tenki sandbox volume create --workspace <workspace-id> --name npm-cache --size 20GBThe CLI requires explicit units:
10GB,10GiB,500MB: accepted- bare
1024: rejected
Current public API validation:
- minimum:
1 MiB - maximum:
100 GiB
List, get, resize, delete
tenki sandbox volume list --workspace <workspace-id>
tenki sandbox volume list --workspace <workspace-id> --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.
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>Go SDK
volume, err := client.CreateVolume(
ctx,
tenkisandbox.WithWorkspaceID(workspaceID),
tenkisandbox.WithVolumeName("npm-cache"),
tenkisandbox.WithVolumeSize(20*tenkisandbox.GB),
)
volume, err = client.WaitVolumeReady(ctx, volume.ID, 3*time.Minute)
volumes, err := client.ListVolumes(ctx, workspaceID)
volume, err = client.GetVolume(ctx, volume.ID)
volume, err = client.ResizeVolume(ctx, volume.ID, 40*tenkisandbox.GB)
err = session.AttachVolume(ctx, volume.ID, "/workspace/cache")
err = session.AttachVolume(ctx, volume.ID, "/workspace/reference", tenkisandbox.WithReadOnly())
err = session.DetachVolume(ctx, volume.ID)
err = client.DeleteVolume(ctx, volume.ID)Useful size constants: tenkisandbox.KB, MB, GB (and KiB, MiB, GiB for binary units).
TypeScript SDK
import { GiB } from "@tenki/sandbox";
const volume = await sandbox.createVolume({
workspaceId: "ws-123",
name: "data-vol",
sizeBytes: 10 * GiB,
});
await session.attachVolume(volume.id, "/mnt/data");
await session.detachVolume(volume.id);Workspace ID lookup
Volumes are workspace-scoped, so you'll need a workspace ID. Use WhoAmI to discover them:
identity, err := client.WhoAmI(ctx)
workspaceID := identity.Workspaces[0].IDSee Identity discovery for the full call.
Snapshots, templates, and volumes
This is a critical contract:
- Snapshots and templates do not auto-reattach the original session's volumes.
- Restored sessions are normal sessions.
- If you need a volume after restore, attach it explicitly on create or with
volume attach.
Patterns
Persistent dev cache
tenki sandbox volume create --workspace <workspace-id> --name pnpm-cache --size 20GB
tenki sandbox create --name dev --volume <volume-id>:/workspace/.pnpm-store
tenki sandbox exec --session <session-id> bash -lc 'pnpm install'A later session reuses the same cache:
tenki sandbox create --name dev-2 --volume <volume-id>:/workspace/.pnpm-storeRead-only reference dataset
tenki sandbox volume create --workspace <workspace-id> --name fixtures --size 10GB
tenki sandbox create --volume <volume-id>:/mnt/fixtures:roMount the same dataset read-only into many concurrent sessions without copy overhead.
Errors to handle
The Go SDK maps service errors to typed values:
ErrVolumeNotFoundErrVolumeInUseErrVolumeLimitExceeded
Pattern:
if errors.Is(err, tenkisandbox.ErrVolumeInUse) {
// detach from the holding session, or wait
}