From aff4b5d1cc4f1dcc6d56ad0e7fe4812f5d82fbfe Mon Sep 17 00:00:00 2001 From: Joe Lombrozo Date: Wed, 17 Jun 2026 19:15:17 -0700 Subject: [PATCH 1/4] Add some docs describing how to migrate from old volumes to new ones --- docs/volumes/migrate.mdx | 100 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 docs/volumes/migrate.mdx diff --git a/docs/volumes/migrate.mdx b/docs/volumes/migrate.mdx new file mode 100644 index 00000000..86074e18 --- /dev/null +++ b/docs/volumes/migrate.mdx @@ -0,0 +1,100 @@ +# Copying Data Between Two Volumes in an E2B Sandbox + +This script spins up a sandbox from the **base** template, mounts two volumes, +and copies all data from a source volume into a destination volume. + +- The **source** volume must already exist. If it doesn't, the script fails. +- The **destination** volume is reused if it already exists, or created if it + doesn't. + +## Prerequisites + +- An E2B account and API key (`E2B_API_KEY` set in your environment). +- The E2B JavaScript SDK installed: `npm install e2b` + +## The script + +Save this as `copy-volume.mts`: + +```ts +import { Sandbox, Volume } from 'e2b' + +const [sourceName, destName] = process.argv.slice(2) +if (!sourceName || !destName) { + throw new Error('Usage: copy-volume ') +} + +async function findVolumeByName(name: string) { + const volumes = await Volume.list() + return volumes.find((v) => v.name === name) ?? null +} + +const sourceInfo = await findVolumeByName(sourceName) +if (!sourceInfo) { + throw new Error(`Source volume "${sourceName}" does not exist`) +} +const sourceVolume = await Volume.connect(sourceInfo.volumeId) + +const destInfo = await findVolumeByName(destName) +const destVolume = destInfo + ? await Volume.connect(destInfo.volumeId) + : await Volume.create(destName) + +function voltype(token: string): string { + const payload = JSON.parse(atob(token.split('.')[1])) + return payload.voltype +} + +console.log(`Source volume type: ${voltype(sourceVolume.token)}`) +console.log(`Destination volume type: ${voltype(destVolume.token)}`) + +const sandbox = await Sandbox.create({ + volumeMounts: { + '/mnt/source': sourceVolume, + '/mnt/dest': destVolume, + }, +}) + +try { + const install = await sandbox.commands.run( + 'sudo apt-get update && sudo apt-get install -y rsync', + ) + if (install.exitCode !== 0) { + throw new Error(`rsync install failed: ${install.stderr}`) + } + + const result = await sandbox.commands.run('rsync -a /mnt/source/ /mnt/dest/') + if (result.exitCode !== 0) { + throw new Error(`Copy failed: ${result.stderr}`) + } + + const listing = await sandbox.commands.run('ls -la /mnt/dest') + console.log(listing.stdout) +} finally { + await sandbox.kill() +} +``` + +## Usage + +Run it with the source and destination volume names: + +```bash +export E2B_API_KEY=your-api-key +npx tsx copy-volume.mts +``` + +For example, to copy everything from `prod-data` into `prod-data-backup`: + +```bash +npx tsx copy-volume.mts prod-data prod-data-backup +``` + +On success it prints a listing of the destination so you can confirm the data +landed. The sandbox is always shut down afterward, but both volumes (and their +data) persist. + +## Sources + +- [E2B Volumes documentation](https://e2b.dev/docs/volumes) +- [E2B JS SDK reference](https://e2b.dev/docs/sdk-reference/js-sdk/v1.13.0/sandbox) From a097f041e44309d4ab77439dfd34348db1dac9f9 Mon Sep 17 00:00:00 2001 From: Tomas Beran Date: Thu, 18 Jun 2026 15:14:42 +0200 Subject: [PATCH 2/4] Add Python example to volume migration guide, list it in Volumes nav, and add descriptions to volume pages --- docs.json | 3 +- docs/volumes.mdx | 1 + docs/volumes/download.mdx | 1 + docs/volumes/info.mdx | 1 + docs/volumes/manage.mdx | 1 + docs/volumes/migrate.mdx | 113 ++++++++++++++++++++++++++++-------- docs/volumes/mount.mdx | 1 + docs/volumes/read-write.mdx | 1 + docs/volumes/upload.mdx | 1 + 9 files changed, 98 insertions(+), 25 deletions(-) diff --git a/docs.json b/docs.json index 60e17679..0d81fff0 100644 --- a/docs.json +++ b/docs.json @@ -197,7 +197,8 @@ "docs/volumes/read-write", "docs/volumes/info", "docs/volumes/upload", - "docs/volumes/download" + "docs/volumes/download", + "docs/volumes/migrate" ] }, { diff --git a/docs/volumes.mdx b/docs/volumes.mdx index 8f98d378..35e8ba49 100644 --- a/docs/volumes.mdx +++ b/docs/volumes.mdx @@ -1,5 +1,6 @@ --- title: "Volumes" +description: "Persistent storage that exists independently of sandboxes and can be mounted across multiple sandboxes." sidebarTitle: Overview --- diff --git a/docs/volumes/download.mdx b/docs/volumes/download.mdx index e2f0ef2a..319d7d64 100644 --- a/docs/volumes/download.mdx +++ b/docs/volumes/download.mdx @@ -1,5 +1,6 @@ --- title: "Download data from volume" +description: "Download files from a volume to your local filesystem using the readFile method." sidebarTitle: Download data --- diff --git a/docs/volumes/info.mdx b/docs/volumes/info.mdx index 91c3c7ef..2700a43e 100644 --- a/docs/volumes/info.mdx +++ b/docs/volumes/info.mdx @@ -1,5 +1,6 @@ --- title: "Get information about a file or directory" +description: "Get metadata, check existence, and update permissions for files and directories in a volume." sidebarTitle: File & directory metadata --- diff --git a/docs/volumes/manage.mdx b/docs/volumes/manage.mdx index 08bbd6c0..6794f506 100644 --- a/docs/volumes/manage.mdx +++ b/docs/volumes/manage.mdx @@ -1,5 +1,6 @@ --- title: "Managing volumes" +description: "Create, connect to, list, inspect, and destroy volumes with the E2B SDK." --- ## Create a volume diff --git a/docs/volumes/migrate.mdx b/docs/volumes/migrate.mdx index 86074e18..bacdef88 100644 --- a/docs/volumes/migrate.mdx +++ b/docs/volumes/migrate.mdx @@ -1,22 +1,34 @@ -# Copying Data Between Two Volumes in an E2B Sandbox +--- +title: "Migrating data between volumes" +description: "Copy all data from one E2B volume to another by mounting both into a sandbox and using rsync." +--- -This script spins up a sandbox from the **base** template, mounts two volumes, -and copies all data from a source volume into a destination volume. +When you need to move data from an old volume to a new one, the simplest approach is to mount both volumes into a single sandbox and copy the files across. This page walks through a script that does exactly that: it spins up a sandbox from the **base** template, mounts a source and a destination volume, and copies all data from source to destination. - The **source** volume must already exist. If it doesn't, the script fails. -- The **destination** volume is reused if it already exists, or created if it - doesn't. +- The **destination** volume is reused if it already exists, or created if it doesn't. ## Prerequisites -- An E2B account and API key (`E2B_API_KEY` set in your environment). -- The E2B JavaScript SDK installed: `npm install e2b` +You need an E2B account with your API key set as `E2B_API_KEY` in your environment, and the E2B SDK installed. + + +```bash JavaScript & TypeScript +npm install e2b +``` +```bash Python +pip install e2b +``` + ## The script -Save this as `copy-volume.mts`: +The script first resolves both volumes by name, connecting to the source and connecting to (or creating) the destination. It then mounts both into a sandbox and uses [rsync](https://rsync.samba.org/) to copy the data across. Reading the volume type from the token is optional, but it's a handy sanity check that you're copying between the volumes you expect. -```ts +Save this as `copy-volume.mts` (JavaScript & TypeScript) or `copy_volume.py` (Python): + + +```ts JavaScript & TypeScript import { Sandbox, Volume } from 'e2b' const [sourceName, destName] = process.argv.slice(2) @@ -74,27 +86,80 @@ try { await sandbox.kill() } ``` +```python Python +import base64 +import json +import sys -## Usage +from e2b import Sandbox, Volume -Run it with the source and destination volume names: +args = sys.argv[1:] +if len(args) != 2: + raise SystemExit('Usage: copy_volume ') +source_name, dest_name = args -```bash -export E2B_API_KEY=your-api-key -npx tsx copy-volume.mts -``` -For example, to copy everything from `prod-data` into `prod-data-backup`: +def find_volume_by_name(name): + return next((v for v in Volume.list() if v.name == name), None) -```bash -npx tsx copy-volume.mts prod-data prod-data-backup + +def voltype(token): + payload = token.split('.')[1] + payload += '=' * (-len(payload) % 4) # JWT segments omit base64 padding + return json.loads(base64.urlsafe_b64decode(payload))['voltype'] + + +source_info = find_volume_by_name(source_name) +if source_info is None: + raise SystemExit(f'Source volume "{source_name}" does not exist') +source_volume = Volume.connect(source_info.volume_id) + +dest_info = find_volume_by_name(dest_name) +dest_volume = ( + Volume.connect(dest_info.volume_id) if dest_info else Volume.create(dest_name) +) + +print(f'Source volume type: {voltype(source_volume.token)}') +print(f'Destination volume type: {voltype(dest_volume.token)}') + +sandbox = Sandbox.create( + volume_mounts={ + '/mnt/source': source_volume, + '/mnt/dest': dest_volume, + }, +) + +try: + install = sandbox.commands.run( + 'sudo apt-get update && sudo apt-get install -y rsync', + ) + if install.exit_code != 0: + raise RuntimeError(f'rsync install failed: {install.stderr}') + + result = sandbox.commands.run('rsync -a /mnt/source/ /mnt/dest/') + if result.exit_code != 0: + raise RuntimeError(f'Copy failed: {result.stderr}') + + listing = sandbox.commands.run('ls -la /mnt/dest') + print(listing.stdout) +finally: + sandbox.kill() ``` + + +## Usage -On success it prints a listing of the destination so you can confirm the data -landed. The sandbox is always shut down afterward, but both volumes (and their -data) persist. +Run the script with the source and destination volume names. For example, to copy everything from `prod-data` into `prod-data-backup`: -## Sources + +```bash JavaScript & TypeScript +export E2B_API_KEY=your-api-key +npx tsx copy-volume.mts prod-data prod-data-backup +``` +```bash Python +export E2B_API_KEY=your-api-key +python copy_volume.py prod-data prod-data-backup +``` + -- [E2B Volumes documentation](https://e2b.dev/docs/volumes) -- [E2B JS SDK reference](https://e2b.dev/docs/sdk-reference/js-sdk/v1.13.0/sandbox) +On success it prints a listing of the destination so you can confirm the data landed. The sandbox is always shut down afterward, but both volumes (and their data) persist. diff --git a/docs/volumes/mount.mdx b/docs/volumes/mount.mdx index ab02a12d..8f199882 100644 --- a/docs/volumes/mount.mdx +++ b/docs/volumes/mount.mdx @@ -1,5 +1,6 @@ --- title: "Mounting volumes" +description: "Mount one or more volumes to a sandbox at custom paths when creating it." --- You can mount one or more volumes to a sandbox when creating it. The keys of the `volumeMounts` / `volume_mounts` object are the mount paths inside the sandbox. diff --git a/docs/volumes/read-write.mdx b/docs/volumes/read-write.mdx index 43838b6c..cd644e7e 100644 --- a/docs/volumes/read-write.mdx +++ b/docs/volumes/read-write.mdx @@ -1,5 +1,6 @@ --- title: "Read & write files" +description: "Read, write, list, and remove files and directories in a volume with the SDK." sidebarTitle: Read & write --- diff --git a/docs/volumes/upload.mdx b/docs/volumes/upload.mdx index dee64c68..6ac64e48 100644 --- a/docs/volumes/upload.mdx +++ b/docs/volumes/upload.mdx @@ -1,5 +1,6 @@ --- title: "Upload data to volume" +description: "Upload single files or entire directories from your local filesystem to a volume." sidebarTitle: Upload data --- From 75d742782bff064fa49e461e8bbefa7f96988d1d Mon Sep 17 00:00:00 2001 From: Tomas Beran Date: Thu, 18 Jun 2026 16:18:06 +0200 Subject: [PATCH 3/4] Fix JS voltype to decode base64url JWT payloads correctly --- docs/volumes/migrate.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/volumes/migrate.mdx b/docs/volumes/migrate.mdx index bacdef88..be401367 100644 --- a/docs/volumes/migrate.mdx +++ b/docs/volumes/migrate.mdx @@ -53,7 +53,10 @@ const destVolume = destInfo : await Volume.create(destName) function voltype(token: string): string { - const payload = JSON.parse(atob(token.split('.')[1])) + // JWT payloads are base64url and often omit padding, so normalize before decoding + let segment = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/') + segment += '='.repeat((4 - (segment.length % 4)) % 4) + const payload = JSON.parse(atob(segment)) return payload.voltype } From 903f823cc3e4e27c851be653bdcdf1c730900bc5 Mon Sep 17 00:00:00 2001 From: Tomas Beran Date: Thu, 18 Jun 2026 16:24:03 +0200 Subject: [PATCH 4/4] Make volume download description method-agnostic --- docs/volumes/download.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/volumes/download.mdx b/docs/volumes/download.mdx index 319d7d64..aea31cc6 100644 --- a/docs/volumes/download.mdx +++ b/docs/volumes/download.mdx @@ -1,6 +1,6 @@ --- title: "Download data from volume" -description: "Download files from a volume to your local filesystem using the readFile method." +description: "Download files from a volume to your local filesystem with the E2B SDK." sidebarTitle: Download data ---