Skip to content
Cloudflare Docs

Backups

Create point-in-time snapshots of sandbox directories and restore them with copy-on-write overlays.

Methods

createBackup()

Create a point-in-time snapshot of a directory and upload it to R2 storage.

TypeScript
await sandbox.createBackup(options: BackupOptions): Promise<DirectoryBackup>

Parameters:

  • options - Backup configuration (see BackupOptions):
    • dir (required) - Absolute path to the directory to back up (for example, "/workspace")
    • name (optional) - Human-readable name for the backup. Maximum 256 characters, no control characters.
    • ttl (optional) - Time-to-live in seconds until the backup expires. Default: 259200 (3 days). Must be a positive number.

Returns: Promise<DirectoryBackup> containing:

  • id - Unique backup identifier (UUID)
  • dir - Directory that was backed up
JavaScript
import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
// Create a backup of /workspace
const backup = await sandbox.createBackup({ dir: "/workspace" });
// Later, restore the backup
await sandbox.restoreBackup(backup);

How it works:

  1. The container creates a compressed squashfs archive from the directory.
  2. The container uploads the archive directly to R2 using a presigned URL.
  3. Metadata is stored alongside the archive in R2.
  4. The local archive is cleaned up.

Throws:

  • InvalidBackupConfigError - If dir is not absolute, contains .., the BACKUP_BUCKET binding is missing, or the R2 presigned URL credentials are not configured
  • BackupCreateError - If the container fails to create the archive or the upload to R2 fails

restoreBackup()

Restore a previously created backup into a directory using FUSE overlayfs (copy-on-write).

TypeScript
await sandbox.restoreBackup(backup: DirectoryBackup): Promise<RestoreBackupResult>

Parameters:

  • backup - The backup handle returned by createBackup(). Contains id and dir. (see DirectoryBackup)

Returns: Promise<RestoreBackupResult> containing:

  • success - Whether the restore succeeded
  • dir - Directory that was restored
  • id - Backup ID that was restored
JavaScript
// Create a named backup with 24-hour TTL
const backup = await sandbox.createBackup({
dir: "/workspace",
name: "before-refactor",
ttl: 86400,
});
// Store the handle for later use
await env.KV.put(`backup:${userId}`, JSON.stringify(backup));

How it works:

  1. Metadata is downloaded from R2 and the TTL is checked. If expired, an error is thrown (with a 60-second buffer).
  2. The container downloads the archive directly from R2 using a presigned URL.
  3. The container mounts the squashfs archive with FUSE overlayfs.

Throws:

  • InvalidBackupConfigError - If backup.id is missing or not a valid UUID, or backup.dir is invalid
  • BackupNotFoundError - If the backup metadata or archive is not found in R2
  • BackupExpiredError - If the backup TTL has elapsed
  • BackupRestoreError - If the container fails to restore

Usage patterns

Checkpoint and restore

Use backups as checkpoints before risky operations.

JavaScript
// Save checkpoint before risky operation
const checkpoint = await sandbox.createBackup({ dir: "/workspace" });
try {
await sandbox.exec("npm install some-experimental-package");
await sandbox.exec("npm run build");
} catch (error) {
// Restore to the checkpoint if something goes wrong
await sandbox.restoreBackup(checkpoint);
}

Error handling

JavaScript
import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
try {
const backup = await sandbox.createBackup({ dir: "/workspace" });
console.log(`Backup created: ${backup.id}`);
} catch (error) {
if (error.code === "INVALID_BACKUP_CONFIG") {
console.error("Configuration error:", error.message);
} else if (error.code === "BACKUP_CREATE_FAILED") {
console.error("Backup failed:", error.message);
}
}

Behavior

  • Concurrent backup and restore operations on the same sandbox are automatically serialized.
  • The returned DirectoryBackup handle is serializable — store it in KV, D1, or Durable Object storage.
  • Overlapping backups are independent. Restoring a parent directory overwrites subdirectory mounts.

TTL enforcement

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.

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 on your backup bucket. Without a lifecycle rule, expired backups continue to consume R2 storage.

Types

BackupOptions

TypeScript
interface BackupOptions {
dir: string;
name?: string;
ttl?: number;
}

Fields:

  • dir (required) - Absolute path to the directory to back up
  • name (optional) - Human-readable backup name. Maximum 256 characters, no control characters.
  • ttl (optional) - Time-to-live in seconds. Default: 259200 (3 days). Must be a positive number.

DirectoryBackup

TypeScript
interface DirectoryBackup {
readonly id: string;
readonly dir: string;
}

Fields:

  • id - Unique backup identifier (UUID)
  • dir - Directory that was backed up

RestoreBackupResult

TypeScript
interface RestoreBackupResult {
success: boolean;
dir: string;
id: string;
}

Fields:

  • success - Whether the restore succeeded
  • dir - Directory that was restored
  • id - Backup ID that was restored