Create point-in-time snapshots of sandbox directories and restore them from R2.
For setup, restore workflows, and generated-cache exclusions, refer to Backup and restore. For overlay semantics, refer to Directory backups.
Create a snapshot of a directory and upload it to R2.
await sandbox.createBackup(options: BackupOptions): Promise<DirectoryBackup>Parameters:
options- Backup configuration (seeBackupOptions):dir(required) - Absolute path to back up. Must be under/workspace,/home,/tmp,/var/tmp, or/app.name(optional) - Human-readable name. Maximum 256 characters. Control characters are rejected.ttl(optional) - Time-to-live in seconds. Default:259200(3 days). Must be a positive number.gitignore(optional) - Whentrue, exclude paths matching.gitignorerules ifdiris inside a git repository. Default:false. If the directory is not in a git repository, no git exclusions apply. Ifgitis not installed, the SDK logs a warning and continues without git-based exclusions.excludes(optional) - Glob patterns to omit from the archive. Passed tomksquashfsas wildcard excludes.**globstars are normalized automatically. Default:[].localBucket(optional) - Whentrue, use theBACKUP_BUCKETR2 binding instead of presigned URLs. Intended forwrangler dev. Default:false.compression(optional) - Archive compression. Default format:lz4. Default threads:8. Format must begzip,lz4, orzstd.threadsmust be a positive integer.multipart(optional) - Use parallel multipart upload for large archives. Default:true.
Returns: Promise<DirectoryBackup> containing:
id- Unique backup identifier (UUID)dir- Directory that was backed uplocalBucket(optional) - Whether the backup used local R2 binding mode
import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
const backup = await sandbox.createBackup({ dir: "/workspace" });
await sandbox.restoreBackup(backup);import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
const backup = await sandbox.createBackup({ dir: "/workspace" });
await sandbox.restoreBackup(backup);How it works:
In production:
- The container creates a compressed squashfs archive.
- The container uploads the archive to R2 with a presigned URL.
- Metadata is stored alongside the archive in R2.
- The local archive is deleted.
With localBucket: true:
- The container creates a compressed squashfs archive.
- The archive is uploaded through the
BACKUP_BUCKETR2 binding. - Metadata is stored alongside the archive in R2.
- The local archive is deleted.
Throws:
InvalidBackupConfigError- Ifdiris not an allowed absolute path, theBACKUP_BUCKETbinding is missing, or (in production) R2 presigned URL credentials are not configuredBackupCreateError- If archive creation or the upload to R2 fails
Restore a previously created backup.
await sandbox.restoreBackup(backup: DirectoryBackup): Promise<RestoreBackupResult>Parameters:
backup- Handle returned bycreateBackup(). Containsidanddir. Restore writes intobackup.dir, which may differ from the original backup path. (seeDirectoryBackup)
Returns: Promise<RestoreBackupResult> containing:
success- Whether the restore succeededdir- Directory that was restoredid- Backup ID that was restored
await sandbox.restoreBackup(backup);await sandbox.restoreBackup(backup);How it works:
In production:
- Metadata is downloaded from R2 and the TTL is checked, with a 60-second buffer. An expired backup throws.
- The container downloads the archive from R2 with a presigned URL.
- The container mounts the archive with FUSE overlayfs.
With localBucket: true:
- Metadata is downloaded from the
BACKUP_BUCKETbinding and the TTL is checked. - The archive is downloaded from the R2 binding.
- The archive is extracted with
unsquashfs.
Throws:
InvalidBackupConfigError- Ifbackup.idis missing or not a UUID, orbackup.diris invalidBackupNotFoundError- If the metadata or archive is not in R2BackupExpiredError- If the TTL has elapsedBackupRestoreError- If the container fails to restore
- Concurrent backup and restore operations on the same sandbox are serialized.
DirectoryBackupis serializable. Store it in KV, D1, or Durable Object storage.- Overlapping backups are independent. Restoring a parent directory overwrites subdirectory mounts. Restore the parent first when restoring both.
ttlis enforced at restore time only. Expired objects remain in R2 until you delete them or an R2 lifecycle rule removes them.- Backup objects use
backups/{id}/data.sqshandbackups/{id}/meta.json.
interface BackupCompressionOptions {
format?: "gzip" | "lz4" | "zstd";
threads?: number;
}
interface BackupOptions {
dir: string;
name?: string;
ttl?: number;
gitignore?: boolean;
excludes?: string[];
localBucket?: boolean;
compression?: BackupCompressionOptions;
multipart?: boolean;
}Fields:
dir(required) - Absolute path under/workspace,/home,/tmp,/var/tmp, or/appname(optional) - Human-readable name. Maximum 256 characters. No control characters.ttl(optional) - Time-to-live in seconds. Default:259200(3 days). Must be a positive number.gitignore(optional) - Whentrue, exclude.gitignorematches ifdiris inside a git repository. Default:false.excludes(optional) - Glob patterns to omit. Example:['node_modules/.cache', '*.log']. Refer to Exclude generated caches.localBucket(optional) - Use theBACKUP_BUCKETbinding instead of presigned URLs. Default:false.compression(optional) -formatdefaults tolz4.threadsdefaults to8.multipart(optional) - Parallel multipart upload. Default:true.
interface DirectoryBackup {
readonly id: string;
readonly dir: string;
readonly localBucket?: boolean;
}Fields:
id- Unique backup identifier (UUID)dir- Directory to restore intolocalBucket(optional) - Whether the backup used local R2 binding mode
interface RestoreBackupResult {
success: boolean;
dir: string;
id: string;
}Fields:
success- Whether the restore succeededdir- Directory that was restoredid- Backup ID that was restored
- Backup and restore - Setup and restore workflows
- Directory backups - Overlay restore and
EXDEV - Storage API - Mount S3-compatible buckets
- Files API - Read and write files
- Wrangler configuration - Configure bindings