--- title: Persistence description: Keeping sandbox state beyond the life of a sandbox. --- The writable filesystem of a Daytona sandbox exists only while the sandbox does. Deleting a sandbox discards its filesystem, memory, and processes. To keep state beyond the sandbox, capture it as an image or a snapshot, or write it to storage outside the sandbox. Snapshots on this page are Linux snapshots: they save memory and disk and restore on the runner that captured them. A persistent macOS sandbox keeps its disk across stop and start, and a macOS snapshot saves only the disk of a stopped persistent sandbox, as an image that restores on any Mac of the region. See [macOS filesystem persistence](/docs/v2/macos-sandboxes#filesystem-persistence). | **Layer** | **What is preserved** | **Mechanisms** | | -------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------- | | **Filesystem** | Files, packages, repositories, build artifacts | • [**Image capture**](/docs/v2/images#capture-images) | | **Memory** | Processes, connections, states | • [**Snapshots**](/docs/v2/snapshots#create-snapshots) | | **External storage** | Data that outlives any single sandbox | • [**Volumes**](/docs/v2/volumes) | ## Use cases Persistence is used when the state of a sandbox must survive past the sandbox itself. - **Prepared environments**: an image capture keeps installed packages, repositories, and configuration. New sandboxes start from the image with nothing to reinstall or rebuild. - **Runs from a known state**: a snapshot copies a configured sandbox, so several runs or every agent in a fleet start from the same state. - **Results that outlive the sandbox**: a sandbox writes its output to storage outside the sandbox, such as your own object storage, and the data stays after the sandbox is deleted. ## Filesystem persistence Filesystem persistence keeps the contents of a sandbox's disk: files, installed packages, cloned repositories, and build artifacts. Delete discards a sandbox's filesystem, and a host failure loses an ephemeral sandbox. A [persistent](/docs/v2/linux-sandboxes#persistent) Linux sandbox keeps its write layer on network storage: a host failure leaves it stopped with its disk, and a start places it on another host. Capture an [**image**](/docs/v2/images#capture-images) or write results to storage outside the sandbox to keep them. ```python from daytona import Daytona daytona = Daytona() sandbox = daytona.v2.create() # Prepare the environment to capture sandbox.process.run("pip install requests") # An image capture keeps the filesystem beyond the sandbox image = sandbox.snapshot_filesystem(name="my-env") # Capture runs asynchronously; wait until the image is ready while image.status not in ("ready", "failed"): image = daytona.v2.images.get(image.id, wait="ready") if image.status == "failed": raise RuntimeError(image.error) # Delete discards the sandbox; the image remains sandbox.delete() # Any number of new sandboxes can start from the same image clone = daytona.v2.create("my-env") response = clone.process.run("pip show requests") print(response.stdout) # requests is already installed ``` ```typescript import { Daytona } from '@daytona/sdk' const daytona = new Daytona() const sandbox = await daytona.v2.create() // Prepare the environment to capture await sandbox.process.run('pip install requests') // An image capture keeps the filesystem beyond the sandbox let image = await sandbox.snapshotFilesystem({ name: 'my-env' }) // Capture runs asynchronously; wait until the image is ready while (image.status !== 'ready' && image.status !== 'failed') { image = await daytona.v2.images.get(image.id, { wait: 'ready' }) } if (image.status === 'failed') throw new Error(image.error ?? 'image capture failed') // Delete discards the sandbox; the image remains await sandbox.delete() // Any number of new sandboxes can start from the same image const clone = await daytona.v2.create('my-env') const response = await clone.process.run('pip show requests') console.log(response.stdout) // requests is already installed ``` ```bash daytona sandbox create # Prepare the environment to capture daytona sandbox exec {sandboxId} -- pip install requests # An image capture keeps the filesystem beyond the sandbox; the command waits until the image is ready daytona image capture {sandboxId} --name my-env # Delete discards the sandbox; the image remains daytona sandbox delete {sandboxId} --yes # Any number of new sandboxes can start from the same image daytona sandbox create --image my-env daytona sandbox exec {cloneId} -- pip show requests ``` ```bash curl 'https://api.daytona.com/sandboxes' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{}' # Prepare the environment to capture curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes?waitMs=60000&logs=text' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{"kind": "exec", "shellCommand": "pip install requests"}' # An image capture keeps the filesystem beyond the sandbox curl 'https://api.daytona.com/sandboxes/{sandboxId}/images' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{"name": "my-env"}' # Capture runs asynchronously; block until the image is ready curl 'https://api.daytona.com/images/{imageId}?wait=ready' \ --header 'Authorization: Bearer YOUR_API_KEY' # Delete discards the sandbox; the image remains curl 'https://api.daytona.com/sandboxes/{sandboxId}' \ --request DELETE \ --header 'Authorization: Bearer YOUR_API_KEY' # Any number of new sandboxes can start from the same image curl 'https://api.daytona.com/sandboxes' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{"image": "my-env"}' ``` - The wait request returns at `ready` or `failed`, or after 30 seconds. Repeat it until the image status is `ready`. ## Memory persistence Memory persistence keeps the runtime state of a sandbox: running processes, open connections, and everything loaded in RAM. A [**snapshot**](/docs/v2/snapshots#create-snapshots) captures the memory and disk of a running Linux sandbox served from a warm template, and a sandbox restored from it continues from the point it was captured, with processes still running, instead of starting from a clean boot. macOS snapshots do not save memory. ```python from daytona import Daytona daytona = Daytona() # Snapshot capture needs a sandbox served from a warm template sandbox = daytona.v2.create("python-3.12") # Start a server that runs in the background sandbox.process.start("python3 -m http.server 8000") # A snapshot keeps memory and running processes beyond the sandbox snapshot = sandbox.create_snapshot("my-snapshot") # Capture runs asynchronously; wait until the snapshot is ready while snapshot.status not in ("ready", "failed"): snapshot = daytona.v2.snapshots.get(snapshot.id, wait="ready") if snapshot.status == "failed": raise RuntimeError(snapshot.error) # Delete discards the sandbox; the snapshot remains sandbox.delete() # The restored sandbox resumes the server without starting it again restored = daytona.v2.create_from_snapshot("my-snapshot") response = restored.process.run(argv=[ "python3", "-c", "import urllib.request; print(urllib.request.urlopen('http://localhost:8000').status)", ]) print(response.stdout) # 200 ``` ```typescript import { Daytona } from '@daytona/sdk' const daytona = new Daytona() // Snapshot capture needs a sandbox served from a warm template const sandbox = await daytona.v2.create('python-3.12') // Start a server that runs in the background await sandbox.process.start('python3 -m http.server 8000') // A snapshot keeps memory and running processes beyond the sandbox let snapshot = await sandbox.createSnapshot('my-snapshot') // Capture runs asynchronously; wait until the snapshot is ready while (snapshot.status !== 'ready' && snapshot.status !== 'failed') { snapshot = await daytona.v2.snapshots.get(snapshot.id, { wait: 'ready' }) } if (snapshot.status === 'failed') throw new Error(snapshot.error ?? 'snapshot capture failed') // Delete discards the sandbox; the snapshot remains await sandbox.delete() // The restored sandbox resumes the server without starting it again const restored = await daytona.v2.createFromSnapshot('my-snapshot') const response = await restored.process.run({ argv: [ 'python3', '-c', "import urllib.request; print(urllib.request.urlopen('http://localhost:8000').status)", ], }) console.log(response.stdout) // 200 ``` ```bash # Snapshot capture needs a sandbox served from a warm template daytona sandbox create --image python-3.12 # Start a server that runs in the background daytona sandbox exec {sandboxId} -- bash -c 'nohup python3 -m http.server 8000 > /tmp/server.log 2>&1 &' # A snapshot keeps memory and running processes beyond the sandbox; the command waits until the snapshot is ready daytona snapshot create {sandboxId} --name my-snapshot # Delete discards the sandbox; the snapshot remains daytona sandbox delete {sandboxId} --yes # The restored sandbox resumes the server without starting it again daytona sandbox create-from-snapshot my-snapshot daytona sandbox exec {restoredId} -- python3 -c "import urllib.request; print(urllib.request.urlopen('http://localhost:8000').status)" ``` ```bash # Snapshot capture needs a sandbox served from a warm template curl 'https://api.daytona.com/sandboxes' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{"image": "python-3.12"}' # Start a server that runs in the background curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{"kind": "exec", "shellCommand": "python3 -m http.server 8000"}' # A snapshot keeps memory and running processes beyond the sandbox curl 'https://api.daytona.com/sandboxes/{sandboxId}/snapshots' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{"name": "my-snapshot"}' # Capture runs asynchronously; block until the snapshot is ready curl 'https://api.daytona.com/snapshots/{snapshotId}?wait=ready&timeout_ms=60000' \ --header 'Authorization: Bearer YOUR_API_KEY' # Delete discards the sandbox; the snapshot remains curl 'https://api.daytona.com/sandboxes/{sandboxId}' \ --request DELETE \ --header 'Authorization: Bearer YOUR_API_KEY' # The restored sandbox resumes the server without starting it again curl 'https://api.daytona.com/sandboxes/from-snapshot' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{"snapshot": "my-snapshot"}' curl 'https://toolbox.daytona.com/toolbox/{restoredId}/processes?waitMs=5000&logs=text' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{"kind": "exec", "argv": ["python3", "-c", "import urllib.request; print(urllib.request.urlopen(\"http://localhost:8000\").status)"]}' ``` - The wait request returns at `ready` or `failed`, or after `timeout_ms`. Repeat it until the snapshot status is `ready`. ## Persistence beyond a sandbox Deleting a sandbox deletes its state. To persist state beyond the sandbox itself, capture it as an **image** or a **snapshot**, or write it to storage outside the sandbox. ### Images and snapshots An [image capture](/docs/v2/images#capture-images) saves the filesystem of a running Linux or Windows sandbox under a name, and new sandboxes boot from it. A [snapshot](/docs/v2/snapshots) saves the memory and disk of a running Linux sandbox served from a warm template, and a restored sandbox resumes the captured processes. A snapshot restores only on the runner that captured it. A macOS snapshot is an image of the disk of a stopped persistent sandbox and restores on any Mac of the region ([macOS snapshots](/docs/v2/macos-sandboxes#filesystem-snapshot)). A snapshot covers the sandbox's own state. A restored sandbox gets a fresh IP and port mappings. Deleting the source sandbox does not delete its snapshots. ### Volumes [Volumes](/docs/v2/volumes) for Daytona v2 are coming soon. ## Retention and lifecycle When one of a sandbox's [timeouts](/docs/v2/linux-sandboxes#timeouts) elapses, Daytona runs the action set for it in `on_timeout` or `on_idle_timeout`: `stop`, `pause`, or `delete`. The timeouts and actions come from the sandbox's lifecycle policy, the project's lifecycle defaults, or the platform default timeout. Two platform events also act on sandboxes without a request from you: - **Spot reclaim**: when the platform reclaims a [spot](/docs/v2/preemption#spot-sandboxes) sandbox, an ephemeral sandbox is deleted, a persistent Linux sandbox is paused, and a persistent sandbox of another class, such as Windows, is stopped with its disk kept. Reclaimed sandboxes are stamped with `spot_evicted_at`. See [preemption](/docs/v2/preemption). - **Host failure**: an ephemeral sandbox on a failed host is lost. A persistent Linux sandbox is stopped with its disk. ```python from daytona import Daytona daytona = Daytona() # Create, use, delete sandbox = daytona.v2.create() # Run the workload and read the results before deleting response = sandbox.process.run("python3 -c 'print(1 + 1)'") print(response.stdout) # Delete discards the sandbox and all of its state sandbox.delete() ``` ```typescript import { Daytona } from '@daytona/sdk' const daytona = new Daytona() // Create, use, delete const sandbox = await daytona.v2.create() // Run the workload and read the results before deleting const response = await sandbox.process.run("python3 -c 'print(1 + 1)'") console.log(response.stdout) // Delete discards the sandbox and all of its state await sandbox.delete() ``` ```bash # Create, use, delete daytona sandbox create # Run the workload and read the results before deleting daytona sandbox exec {sandboxId} -- python3 -c 'print(1 + 1)' # Delete discards the sandbox and all of its state daytona sandbox delete {sandboxId} --yes ``` ```bash # Create, use, delete curl 'https://api.daytona.com/sandboxes' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{}' # Run the workload and read the results before deleting curl 'https://toolbox.daytona.com/toolbox/{sandboxId}/processes?waitMs=5000&logs=text' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{"kind": "exec", "shellCommand": "python3 -c '\''print(1 + 1)'\''"}' # Delete discards the sandbox and all of its state curl 'https://api.daytona.com/sandboxes/{sandboxId}' \ --request DELETE \ --header 'Authorization: Bearer YOUR_API_KEY' ```