# Persistent Volumes (https://tenki.cloud/docs/sandbox/volumes)

> For the complete documentation index, see [llms.txt](https://tenki.cloud/llms.txt)

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 [#volume-lifecycle]

Pick your tool, and the selection syncs across the rest of the page.

Create [#create]

**CLI**
```bash
tenki sandbox volume create --name npm-cache --size 20GB
```

The CLI requires explicit units:

* `10GB`, `10GiB`, `500MB`: accepted
* bare `1024`: rejected

**TypeScript**
```typescript
import { GiB } from "@tenkicloud/sandbox";

const volume = await sandbox.createVolume({
  name: "data-vol",
  sizeBytes: 10 * GiB,
});

await sandbox.waitVolumeReady(volume.id);
```

**Go**
```go
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).

**Python**
```python
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 [#list-get-resize-delete]

**TypeScript**
```typescript
const volumes = await sandbox.listVolumes();
const volume = await sandbox.getVolume(volumeId);
await sandbox.resizeVolume(volumeId, 40 * GiB);
await sandbox.deleteVolume(volumeId);
```

**Python**
```python
volumes = client.volumes.list()
volume = client.volumes.get(volume_id)
volume = client.volumes.resize(volume_id, 40 * GB)
client.volumes.delete(volume_id)
```

**Go**
```go
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)
```

**CLI**
```bash
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-to-sessions]

Attach at create time [#attach-at-create-time]

**CLI**
```bash
tenki sandbox create \
  --volume <volume-id>:/workspace/cache \
  --volume <other-id>:/workspace/reference:ro
```

The optional `:ro` suffix mounts the volume read-only.

**TypeScript**
```typescript
const session = await sandbox.createAndWait({
  volumes: [
    { volumeId: cacheVolumeId, mountPath: "/workspace/cache" },
    { volumeId: refVolumeId, mountPath: "/workspace/reference", readOnly: true },
  ],
});
```

**Go**
```go
session, err := client.Create(
  ctx,
  tenkisandbox.WithVolume(cacheVolumeID, "/workspace/cache"),
  tenkisandbox.WithVolume(refVolumeID, "/workspace/reference", tenkisandbox.WithReadOnly()),
)
```

**Python**
```python
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 [#attach-to-a-running-session]

**CLI**
```bash
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>
```

**TypeScript**
```typescript
await session.attachVolume(volume.id, "/mnt/data");
await session.detachVolume(volume.id);
```

**Go**
```go
err := session.AttachVolume(ctx, volumeID, "/workspace/cache")
err = session.AttachVolume(ctx, refVolumeID, "/workspace/reference", tenkisandbox.WithReadOnly())
err = session.DetachVolume(ctx, volumeID)
```

**Python**
```python
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 [#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-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](/docs/sandbox/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 [#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](/docs/sandbox/sdk#errors) for the names in each language.
