Skip to content

Commit d7b475e

Browse files
ask-bonk[bot]whoiskatrinOxyjun
authored
Add Sandbox backup/restore API docs (#28449)
* [Sandbox] Add docs for backup and restore API Add documentation for the new createBackup() and restoreBackup() methods introduced in cloudflare/sandbox-sdk#396. - New API reference page: /sandbox/api/backups/ - New how-to guide: /sandbox/guides/backup-restore/ - Updated API index with Backups card - Updated Wrangler config page with BACKUP_BUCKET R2 binding docs Closes #28448 * [Sandbox] Update backup docs for presigned URL flow, TTL enforcement, and R2 cleanup examples * chore: trigger rebuild * [Sandbox] Fix backup docs to match SDK PR #396: correct default TTL to 3 days and RestoreBackupResult.id field name * chore: trigger rebuild * Apply suggestions from code review * Update src/content/docs/sandbox/api/backups.mdx * Update src/content/docs/sandbox/api/backups.mdx * Fix code formatting and number steps Co-authored-by: Oxyjun <Oxyjun@users.noreply.github.com> * chore: re-trigger CI --------- Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com> Co-authored-by: katereznykova <kreznykova@cloudflare.com> Co-authored-by: Jun Lee <junlee@cloudflare.com> Co-authored-by: Oxyjun <Oxyjun@users.noreply.github.com>
1 parent 5d82238 commit d7b475e

4 files changed

Lines changed: 672 additions & 0 deletions

File tree

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
---
2+
title: Backups
3+
pcx_content_type: concept
4+
sidebar:
5+
order: 5
6+
---
7+
8+
import { TypeScriptExample } from "~/components";
9+
10+
Create point-in-time snapshots of sandbox directories and restore them with copy-on-write overlays.
11+
12+
## Methods
13+
14+
### `createBackup()`
15+
16+
Create a point-in-time snapshot of a directory and upload it to R2 storage.
17+
18+
```ts
19+
await sandbox.createBackup(options: BackupOptions): Promise<DirectoryBackup>
20+
```
21+
22+
**Parameters**:
23+
24+
- `options` - Backup configuration (see [`BackupOptions`](#backupoptions)):
25+
- `dir` (required) - Absolute path to the directory to back up (for example, `"/workspace"`)
26+
- `name` (optional) - Human-readable name for the backup. Maximum 256 characters, no control characters.
27+
- `ttl` (optional) - Time-to-live in seconds until the backup expires. Default: `259200` (3 days). Must be a positive number.
28+
29+
**Returns**: `Promise<DirectoryBackup>` containing:
30+
31+
- `id` - Unique backup identifier (UUID)
32+
- `dir` - Directory that was backed up
33+
34+
<TypeScriptExample>
35+
36+
```ts
37+
import { getSandbox } from "@cloudflare/sandbox";
38+
39+
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
40+
41+
// Create a backup of /workspace
42+
const backup = await sandbox.createBackup({ dir: "/workspace" });
43+
44+
// Later, restore the backup
45+
await sandbox.restoreBackup(backup);
46+
```
47+
48+
</TypeScriptExample>
49+
50+
**How it works**:
51+
52+
1. The container creates a compressed squashfs archive from the directory.
53+
2. The container uploads the archive directly to R2 using a presigned URL.
54+
3. Metadata is stored alongside the archive in R2.
55+
4. The local archive is cleaned up.
56+
57+
**Throws**:
58+
59+
- `InvalidBackupConfigError` - If `dir` is not absolute, contains `..`, the `BACKUP_BUCKET` binding is missing, or the R2 presigned URL credentials are not configured
60+
- `BackupCreateError` - If the container fails to create the archive or the upload to R2 fails
61+
62+
:::note[R2 binding and credentials required]
63+
You must configure a `BACKUP_BUCKET` R2 binding and R2 presigned URL credentials (`R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`, `CLOUDFLARE_ACCOUNT_ID`, `BACKUP_BUCKET_NAME`) in your `wrangler.jsonc` before using backup methods. Refer to the [Wrangler configuration](/sandbox/configuration/wrangler/) for binding setup.
64+
:::
65+
66+
:::caution[Partial writes]
67+
Partially-written files may not be captured consistently. Only completed writes are guaranteed to be included in the backup.
68+
:::
69+
70+
---
71+
72+
### `restoreBackup()`
73+
74+
Restore a previously created backup into a directory using FUSE overlayfs (copy-on-write).
75+
76+
```ts
77+
await sandbox.restoreBackup(backup: DirectoryBackup): Promise<RestoreBackupResult>
78+
```
79+
80+
**Parameters**:
81+
82+
- `backup` - The backup handle returned by `createBackup()`. Contains `id` and `dir`. (see [`DirectoryBackup`](#directorybackup))
83+
84+
**Returns**: `Promise<RestoreBackupResult>` containing:
85+
86+
- `success` - Whether the restore succeeded
87+
- `dir` - Directory that was restored
88+
- `id` - Backup ID that was restored
89+
90+
<TypeScriptExample>
91+
92+
```ts
93+
// Create a named backup with 24-hour TTL
94+
const backup = await sandbox.createBackup({
95+
dir: "/workspace",
96+
name: "before-refactor",
97+
ttl: 86400,
98+
});
99+
100+
// Store the handle for later use
101+
await env.KV.put(`backup:${userId}`, JSON.stringify(backup));
102+
```
103+
104+
</TypeScriptExample>
105+
106+
**How it works**:
107+
108+
1. Metadata is downloaded from R2 and the TTL is checked. If expired, an error is thrown (with a 60-second buffer).
109+
2. The container downloads the archive directly from R2 using a presigned URL.
110+
3. The container mounts the squashfs archive with FUSE overlayfs.
111+
112+
**Throws**:
113+
114+
- `InvalidBackupConfigError` - If `backup.id` is missing or not a valid UUID, or `backup.dir` is invalid
115+
- `BackupNotFoundError` - If the backup metadata or archive is not found in R2
116+
- `BackupExpiredError` - If the backup TTL has elapsed
117+
- `BackupRestoreError` - If the container fails to restore
118+
119+
:::note[Copy-on-write]
120+
Restore uses copy-on-write semantics. The backup is mounted as a read-only lower layer, and new writes go to a writable upper layer. The backup can be restored into a different directory than the original.
121+
:::
122+
123+
:::caution[Ephemeral mount]
124+
The FUSE mount is lost when the sandbox sleeps or restarts. Re-restore from the backup handle to recover. Stop processes writing to the target directory before restoring.
125+
:::
126+
127+
## Usage patterns
128+
129+
### Checkpoint and restore
130+
131+
Use backups as checkpoints before risky operations.
132+
133+
<TypeScriptExample>
134+
135+
```ts
136+
// Save checkpoint before risky operation
137+
const checkpoint = await sandbox.createBackup({ dir: "/workspace" });
138+
139+
try {
140+
await sandbox.exec("npm install some-experimental-package");
141+
await sandbox.exec("npm run build");
142+
} catch (error) {
143+
// Restore to the checkpoint if something goes wrong
144+
await sandbox.restoreBackup(checkpoint);
145+
}
146+
```
147+
148+
</TypeScriptExample>
149+
150+
### Error handling
151+
152+
<TypeScriptExample>
153+
154+
```ts
155+
import { getSandbox } from "@cloudflare/sandbox";
156+
157+
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
158+
159+
try {
160+
const backup = await sandbox.createBackup({ dir: "/workspace" });
161+
console.log(`Backup created: ${backup.id}`);
162+
} catch (error) {
163+
if (error.code === "INVALID_BACKUP_CONFIG") {
164+
console.error("Configuration error:", error.message);
165+
} else if (error.code === "BACKUP_CREATE_FAILED") {
166+
console.error("Backup failed:", error.message);
167+
}
168+
}
169+
```
170+
171+
</TypeScriptExample>
172+
173+
## Behavior
174+
175+
- Concurrent backup and restore operations on the same sandbox are automatically serialized.
176+
- The returned `DirectoryBackup` handle is serializable — store it in KV, D1, or Durable Object storage.
177+
- Overlapping backups are independent. Restoring a parent directory overwrites subdirectory mounts.
178+
179+
### TTL enforcement
180+
181+
The `ttl` value controls when a backup is considered expired. The SDK enforces this at **restore time only** — when you call `restoreBackup()`, the SDK reads the backup metadata from R2 and checks whether the TTL has elapsed. If it has, the restore is rejected with a `BACKUP_EXPIRED` error.
182+
183+
The TTL does **not** automatically delete objects from R2. Expired backup archives and metadata remain in your R2 bucket until you delete them. To automatically clean up expired objects, configure an [R2 object lifecycle rule](/r2/buckets/object-lifecycles/) on your backup bucket. Without a lifecycle rule, expired backups continue to consume R2 storage.
184+
185+
## Types
186+
187+
### `BackupOptions`
188+
189+
```ts
190+
interface BackupOptions {
191+
dir: string;
192+
name?: string;
193+
ttl?: number;
194+
}
195+
```
196+
197+
**Fields**:
198+
199+
- `dir` (required) - Absolute path to the directory to back up
200+
- `name` (optional) - Human-readable backup name. Maximum 256 characters, no control characters.
201+
- `ttl` (optional) - Time-to-live in seconds. Default: `259200` (3 days). Must be a positive number.
202+
203+
### `DirectoryBackup`
204+
205+
```ts
206+
interface DirectoryBackup {
207+
readonly id: string;
208+
readonly dir: string;
209+
}
210+
```
211+
212+
**Fields**:
213+
214+
- `id` - Unique backup identifier (UUID)
215+
- `dir` - Directory that was backed up
216+
217+
### `RestoreBackupResult`
218+
219+
```ts
220+
interface RestoreBackupResult {
221+
success: boolean;
222+
dir: string;
223+
id: string;
224+
}
225+
```
226+
227+
**Fields**:
228+
229+
- `success` - Whether the restore succeeded
230+
- `dir` - Directory that was restored
231+
- `id` - Backup ID that was restored
232+
233+
## Related resources
234+
235+
- [Storage API](/sandbox/api/storage/) - Mount S3-compatible buckets
236+
- [Files API](/sandbox/api/files/) - Read and write files
237+
- [Wrangler configuration](/sandbox/configuration/wrangler/) - Configure bindings

src/content/docs/sandbox/api/index.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ The Sandbox SDK provides a comprehensive API for executing code, managing files,
5454
data storage across sandbox lifecycles.
5555
</LinkTitleCard>
5656

57+
<LinkTitleCard title="Backups" href="/sandbox/api/backups/" icon="clock">
58+
Create point-in-time snapshots of directories and restore them with
59+
copy-on-write overlays. Store backups in R2.
60+
</LinkTitleCard>
61+
5762
<LinkTitleCard title="Sessions" href="/sandbox/api/sessions/" icon="seti:plan">
5863
Create isolated execution contexts within a sandbox. Each session maintains
5964
its own shell state, environment variables, and working directory.

src/content/docs/sandbox/configuration/wrangler.mdx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,46 @@ The Sandbox SDK is built on Cloudflare Containers. Your configuration requires t
5252

5353
The minimal configuration shown above includes all required settings. For detailed configuration options, refer to the [Containers configuration documentation](/workers/wrangler/configuration/#containers).
5454

55+
## Backup storage
56+
57+
To use the [backup and restore API](/sandbox/api/backups/), you need an R2 bucket binding and presigned URL credentials. The container uploads and downloads backup archives directly to/from R2 using presigned URLs, which requires R2 API token credentials.
58+
59+
### 1. Create the R2 bucket
60+
61+
```sh
62+
npx wrangler r2 bucket create my-backup-bucket
63+
```
64+
65+
### 2. Add the binding and environment variables
66+
67+
<WranglerConfig>
68+
```jsonc
69+
{
70+
"vars": {
71+
"BACKUP_BUCKET_NAME": "my-backup-bucket",
72+
"CLOUDFLARE_ACCOUNT_ID": "<YOUR_ACCOUNT_ID>",
73+
},
74+
"r2_buckets": [
75+
{
76+
"binding": "BACKUP_BUCKET",
77+
"bucket_name": "my-backup-bucket",
78+
},
79+
],
80+
}
81+
```
82+
</WranglerConfig>
83+
84+
### 3. Set R2 API credentials as secrets
85+
86+
```sh
87+
npx wrangler secret put R2_ACCESS_KEY_ID
88+
npx wrangler secret put R2_SECRET_ACCESS_KEY
89+
```
90+
91+
Create an R2 API token in the [Cloudflare dashboard](https://dash.cloudflare.com/) under **R2** > **Overview** > **Manage R2 API Tokens**. The token needs **Object Read & Write** permissions for your backup bucket.
92+
93+
The SDK uses these credentials to generate presigned URLs that allow the container to transfer backup archives directly to and from R2. For a complete setup walkthrough, refer to the [backup and restore guide](/sandbox/guides/backup-restore/).
94+
5595
## Troubleshooting
5696

5797
### Binding not found

0 commit comments

Comments
 (0)