diff --git a/EXAMPLES.md b/EXAMPLES.md
index 0d3ff62cc..b9c444cfa 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -9,6 +9,7 @@ Runnable examples live in [`examples/`](./examples).
- [Blueprint with Build Context](#blueprint-with-build-context)
- [Devbox From Blueprint (Run Command, Shutdown)](#devbox-from-blueprint-lifecycle)
+- [Devbox Snapshot and Resume](#devbox-snapshot-resume)
- [MCP Hub + Claude Code + GitHub](#mcp-github-tools)
@@ -70,6 +71,37 @@ uv run pytest -m smoketest tests/smoketests/examples/
**Source:** [`examples/devbox_from_blueprint_lifecycle.py`](./examples/devbox_from_blueprint_lifecycle.py)
+
+## Devbox Snapshot and Resume
+
+**Use case:** Create a devbox, snapshot its disk, resume from the snapshot, and demonstrate that changes in the original devbox do not affect the clone. Uses the async SDK.
+
+**Tags:** `devbox`, `snapshot`, `resume`, `cleanup`, `async`
+
+### Workflow
+- Create a devbox
+- Write a file to the devbox
+- Create a disk snapshot
+- Create a new devbox from the snapshot
+- Modify the file on the original devbox
+- Verify the clone has the original content
+- Shutdown both devboxes and delete the snapshot
+
+### Prerequisites
+- `RUNLOOP_API_KEY`
+
+### Run
+```sh
+uv run python -m examples.devbox_snapshot_resume
+```
+
+### Test
+```sh
+uv run pytest -m smoketest tests/smoketests/examples/
+```
+
+**Source:** [`examples/devbox_snapshot_resume.py`](./examples/devbox_snapshot_resume.py)
+
## MCP Hub + Claude Code + GitHub
diff --git a/examples/devbox_snapshot_resume.py b/examples/devbox_snapshot_resume.py
new file mode 100644
index 000000000..6913731b2
--- /dev/null
+++ b/examples/devbox_snapshot_resume.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env -S uv run python
+"""
+---
+title: Devbox Snapshot and Resume
+slug: devbox-snapshot-resume
+use_case: Create a devbox, snapshot its disk, resume from the snapshot, and demonstrate that changes in the original devbox do not affect the clone. Uses the async SDK.
+workflow:
+ - Create a devbox
+ - Write a file to the devbox
+ - Create a disk snapshot
+ - Create a new devbox from the snapshot
+ - Modify the file on the original devbox
+ - Verify the clone has the original content
+ - Shutdown both devboxes and delete the snapshot
+tags:
+ - devbox
+ - snapshot
+ - resume
+ - cleanup
+ - async
+prerequisites:
+ - RUNLOOP_API_KEY
+run: uv run python -m examples.devbox_snapshot_resume
+test: uv run pytest -m smoketest tests/smoketests/examples/
+---
+"""
+
+from __future__ import annotations
+
+from runloop_api_client import AsyncRunloopSDK
+
+from ._harness import run_as_cli, wrap_recipe
+from .example_types import ExampleCheck, RecipeOutput, RecipeContext
+
+FILE_PATH = "/home/user/welcome.txt"
+ORIGINAL_CONTENT = "hello world!"
+MODIFIED_CONTENT = "original devbox has changed the welcome message"
+
+
+async def recipe(ctx: RecipeContext) -> RecipeOutput:
+ """Create a devbox, snapshot it, resume from snapshot, and verify state isolation."""
+ cleanup = ctx.cleanup
+
+ sdk = AsyncRunloopSDK()
+
+ # Create a devbox
+ dbx_original = await sdk.devbox.create(
+ name="dbx_original",
+ launch_parameters={
+ "resource_size_request": "X_SMALL",
+ "keep_alive_time_seconds": 60 * 5,
+ },
+ )
+ cleanup.add(f"devbox:{dbx_original.id}", dbx_original.shutdown)
+
+ # Write a file to the original devbox
+ await dbx_original.file.write(file_path=FILE_PATH, contents=ORIGINAL_CONTENT)
+
+ # Read and display the file contents
+ cat_original_before = await dbx_original.cmd.exec(f"cat {FILE_PATH}")
+ original_content_before = await cat_original_before.stdout()
+
+ # Create a disk snapshot of the original devbox
+ snapshot = await dbx_original.snapshot_disk(name="my-snapshot")
+ cleanup.add(f"snapshot:{snapshot.id}", snapshot.delete)
+
+ # Create a new devbox from the snapshot
+ dbx_clone = await sdk.devbox.create_from_snapshot(
+ snapshot.id,
+ name="dbx_clone",
+ launch_parameters={
+ "resource_size_request": "X_SMALL",
+ "keep_alive_time_seconds": 60 * 5,
+ },
+ )
+ cleanup.add(f"devbox:{dbx_clone.id}", dbx_clone.shutdown)
+
+ # Modify the file on the original devbox
+ await dbx_original.file.write(file_path=FILE_PATH, contents=MODIFIED_CONTENT)
+
+ # Read the file contents from both devboxes
+ cat_clone = await dbx_clone.cmd.exec(f"cat {FILE_PATH}")
+ clone_content = await cat_clone.stdout()
+
+ # now the original devbox has been modified but the clone has the original message
+ cat_original_after = await dbx_original.cmd.exec(f"cat {FILE_PATH}")
+ original_content_after = await cat_original_after.stdout()
+
+ return RecipeOutput(
+ resources_created=[
+ f"devbox:{dbx_original.id}",
+ f"snapshot:{snapshot.id}",
+ f"devbox:{dbx_clone.id}",
+ ],
+ checks=[
+ ExampleCheck(
+ name="original devbox file created successfully",
+ passed=cat_original_before.exit_code == 0 and original_content_before.strip() == ORIGINAL_CONTENT,
+ details=f'content="{original_content_before.strip()}"',
+ ),
+ ExampleCheck(
+ name="snapshot created successfully",
+ passed=bool(snapshot.id),
+ details=f"snapshotId={snapshot.id}",
+ ),
+ ExampleCheck(
+ name="clone devbox created from snapshot",
+ passed=bool(dbx_clone.id),
+ details=f"cloneId={dbx_clone.id}",
+ ),
+ ExampleCheck(
+ name="clone has original file content (before modification)",
+ passed=cat_clone.exit_code == 0 and clone_content.strip() == ORIGINAL_CONTENT,
+ details=f'cloneContent="{clone_content.strip()}"',
+ ),
+ ExampleCheck(
+ name="original devbox has modified content",
+ passed=cat_original_after.exit_code == 0 and original_content_after.strip() == MODIFIED_CONTENT,
+ details=f'originalContent="{original_content_after.strip()}"',
+ ),
+ ExampleCheck(
+ name="clone and original have divergent state",
+ passed=clone_content.strip() != original_content_after.strip(),
+ details=f'clone="{clone_content.strip()}" vs original="{original_content_after.strip()}"',
+ ),
+ ],
+ )
+
+
+run_devbox_snapshot_resume_example = wrap_recipe(recipe)
+
+
+if __name__ == "__main__":
+ run_as_cli(run_devbox_snapshot_resume_example)
diff --git a/examples/registry.py b/examples/registry.py
index cb6b780a9..cde21979d 100644
--- a/examples/registry.py
+++ b/examples/registry.py
@@ -9,6 +9,7 @@
from .example_types import ExampleResult
from .mcp_github_tools import run_mcp_github_tools_example
+from .devbox_snapshot_resume import run_devbox_snapshot_resume_example
from .blueprint_with_build_context import run_blueprint_with_build_context_example
from .devbox_from_blueprint_lifecycle import run_devbox_from_blueprint_lifecycle_example
@@ -29,6 +30,13 @@
"required_env": ["RUNLOOP_API_KEY"],
"run": run_devbox_from_blueprint_lifecycle_example,
},
+ {
+ "slug": "devbox-snapshot-resume",
+ "title": "Devbox Snapshot and Resume",
+ "file_name": "devbox_snapshot_resume.py",
+ "required_env": ["RUNLOOP_API_KEY"],
+ "run": run_devbox_snapshot_resume_example,
+ },
{
"slug": "mcp-github-tools",
"title": "MCP Hub + Claude Code + GitHub",
diff --git a/llms.txt b/llms.txt
index 07971384f..f88325b10 100644
--- a/llms.txt
+++ b/llms.txt
@@ -11,6 +11,7 @@
## Core Patterns
- [Devbox lifecycle example](examples/devbox_from_blueprint_lifecycle.py): Create blueprint, launch devbox, run commands, cleanup
+- [Devbox snapshot and resume example](examples/devbox_snapshot_resume.py): Snapshot disk, resume from snapshot, verify state isolation
- [MCP GitHub example](examples/mcp_github_tools.py): MCP Hub integration with Claude Code
## API Reference