---
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'
```