# How to attach a persistent volume to a sandbox Create a volume with `runtime.volumes.create({ sizeMiB })`, then pass `volumes: [{ volumeId, path }]` when you create the sandbox. **On Runtime a volume is a disk that outlives every sandbox that uses it, and it is backed up off its server every day.** It costs 153 microdollars per GiB-hour, about $0.11 per GiB per 30-day month, so a 10 GiB cache costs about $1.10 a month; the free trial stores its first 10 GiB of volumes free for as long as you keep them (rates in force since 23 September 2026, [pricing](/docs/pricing#snapshots-images-and-volumes)). ## Create, attach, write ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); const volume = await runtime.volumes.create({ sizeMiB: 10_240, name: "cache" }); await using sbx = await runtime.sandboxes.create({ volumes: [{ volumeId: volume.id, path: "/data" }], }); await sbx.exec("sudo chown runtime /data && echo kept > /data/note.txt && sync"); ``` ```python check from withruntime import Runtime runtime = Runtime() volume = runtime.volumes.create(size_mib=10_240, name="cache") with runtime.sandboxes.create(volumes=[{"volume_id": volume["id"], "path": "/data"}]) as sbx: sbx.exec("sudo chown runtime /data && echo kept > /data/note.txt && sync") ``` ```bash no-run vol=$(runtime volume create --size-mib 10240 --name cache) id=$(runtime sandbox create --volume "${vol}:/data") runtime sandbox exec "${id}" -- sh -c 'sudo chown runtime /data && echo kept > /data/note.txt' runtime sandbox stop "${id}" ``` The first command gives `/data` to the sandbox user, `runtime`, so later commands write to it without `sudo`. The next sandbox that attaches this volume finds `/data/note.txt` where this one left it. ## Read-write to one, read-only to many | Mode | Who can attach it | Use it for | | ------------------ | --------------------------------- | ------------------------------------------------- | | `rw` (the default) | One sandbox at a time | A workspace, a package cache, a database's files | | `snapshot` | Any number, each a read-only copy | A dataset or model weights that many workers read | ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); const weights = await runtime.volumes.create({ sizeMiB: 20_480, name: "weights" }); const workers = await Promise.all( [1, 2, 3].map(() => runtime.sandboxes.create({ volumes: [{ volumeId: weights.id, path: "/models", mode: "snapshot" }], }), ), ); await Promise.all(workers.map((w) => w.stop())); ``` A sandbox takes up to four volumes. ## Facts to plan around | Fact | Detail | | -------------- | ------------------------------------------------------------------------------------ | | Price | 153 microdollars per GiB-hour, on the full size, written or not | | Charged from | The moment it is created, until it is deleted | | Where it lives | One server; a sandbox that uses it is placed on that server | | Daily backup | Off the server, starting an hour after creation, kept 7 days unless you set 1 to 365 | | Backup price | $0.012 per decimal GB per 30-day month, on the bytes stored | | Backups held | Up to 200 per organization at a time | | Snapshots | A sandbox with volumes cannot be snapshotted | ## Back it up and restore it A backup can be made on request too, and a restore is a new volume in the same region, on whichever server has room. The original is untouched ([volume backups](/docs/storage#volume-backups)). ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); const volume = await runtime.volumes.create({ sizeMiB: 10_240, name: "data" }); const backup = await runtime.volumes.backup(volume.id, { retentionDays: 30 }); const restored = await runtime.volumes.restore(backup.id, { name: "data-restored" }); console.log(restored.id); ``` ```bash no-run runtime volume backup "${vol}" --retention-days 30 runtime volume restore "${backup}" --name data-restored ``` Only what changed since an earlier backup of the same volume is stored again. Deleting a volume keeps its backups until each one's retention ends. ## Mistakes to avoid - **Letting the lease end on unsynced writes.** Stopping a sandbox has it write out its volumes first, but a sandbox whose lease runs out stops at once. Run `sync` after writes it must keep. - **Two writers.** A read-write volume attaches to one sandbox at a time. For many readers, attach it as `snapshot`; for many writers, give each its own volume or mount a [bucket](/docs/storage#mount-your-own-bucket). - **Keeping work only on the sandbox's own disk.** That disk has no copy off its server. Keep what you cannot rebuild on a volume. - **Oversizing.** A volume is charged on its full size from creation, so a 100 GiB volume holding 2 GiB costs the same as a full one. Start near what you need. - **Expecting a snapshot to include the volume.** A sandbox with volumes cannot be [snapshotted](/how-to/snapshot-a-sandbox). Snapshot a sandbox without volumes, and attach the volume to each copy you start. ## Where volumes fit A volume suits state that outlives a task: a [per-user dev environment's](/use-cases/per-user-dev-environments) home directory, a [data analysis agent's](/use-cases/data-analysis-agent) datasets, or a package cache shared across runs. To keep a whole machine, memory included, between uses, [pause it](/how-to/pause-and-resume-a-sandbox) instead. Facts on this page were checked on 25 September 2026.