# Snapshots | Modal Docs

- **URL:** https://modal.com/docs/guide/sandbox-snapshots
- **Summary:** Sandboxes support snapshotting, allowing you to save your Sandbox’s state and restore it later. This is useful for:

Copy page

Snapshots
=========

Sandboxes support snapshotting, allowing you to save your Sandbox’s state and restore it later. This is useful for:

*   Reducing startup latency
*   Creating custom environments for your Sandboxes to run in
*   Backing up your Sandbox’s state for debugging
*   Running large-scale experiments with the same initial state
*   Branching your Sandbox’s state to test different code changes independently

Modal currently supports three different kinds of Sandbox snapshots:

1.  [Filesystem Snapshots](https://modal.com/docs/guide/sandbox-snapshots#filesystem-snapshots)
    
2.  [Directory Snapshots](https://modal.com/docs/guide/sandbox-snapshots#directory-snapshots)
    
3.  [Memory Snapshots](https://modal.com/docs/guide/sandbox-snapshots#memory-snapshots)
    

Snapshot Retention 

Different snapshot types have different retention policies:

| Snapshot Type | Default Retention Period |
| --- | --- |
| Filesystem Snapshot | 30 days after creation |
| Directory Snapshot | 30 days after creation |
| Memory Snapshot | 7 days after creation |

Filesystem Snapshots and Directory Snapshots are [Images](https://modal.com/docs/sdk/py/latest/modal.Image)
 and are automatically garbage collected after their TTL expires (30 days by default). You can configure a custom TTL when creating a snapshot, or opt out of expiry entirely to retain snapshots indefinitely. Memory Snapshots expire 7 days after creation and cannot currently be extended.

Here is how to configure custom TTLs for each snapshot type:

PythonJavaScriptGo

    # Filesystem snapshot with custom TTL of 7 days
    image = sb.snapshot_filesystem(ttl=7 * 24 * 3600)
    
    # Filesystem snapshot with no expiry (retain indefinitely, like the pre-v1.5 default)
    image = sb.snapshot_filesystem(ttl=None)
    
    # Directory snapshot with custom TTL of 7 days
    snapshot = sb.snapshot_directory("/project", ttl=7 * 24 * 3600)
    
    # Directory snapshot with no expiry
    snapshot = sb.snapshot_directory("/project", ttl=None)

    // Filesystem snapshot with custom TTL of 7 days
    let image = await sb.snapshotFilesystem({ ttlMs: 7 * 24 * 3600 * 1000 });
    
    // Filesystem snapshot with no expiry (retain indefinitely, like the pre-v0.8.0 default)
    image = await sb.snapshotFilesystem({ ttlMs: null });
    
    // Directory snapshot with custom TTL of 7 days
    let snapshot = await sb.snapshotDirectory("/project", {
      ttlMs: 7 * 24 * 3600 * 1000,
    });
    
    // Directory snapshot with no expiry
    snapshot = await sb.snapshotDirectory("/project", { ttlMs: null });

    // Filesystem snapshot with custom TTL of 7 days
    image, _ := sb.SnapshotFilesystem(ctx, &modal.SandboxSnapshotFilesystemParams{
        TTL: 7 * 24 * time.Hour,
    })
    
    // Filesystem snapshot with no expiry (retain indefinitely, like the pre-v0.8.0 default)
    image, _ = sb.SnapshotFilesystem(ctx, &modal.SandboxSnapshotFilesystemParams{
        TTL: modal.NoExpiryTTL,
    })
    
    // Directory snapshot with custom TTL of 7 days
    snapshot, _ := sb.SnapshotDirectory(ctx, "/project", &modal.SandboxSnapshotDirectoryParams{
        TTL: 7 * 24 * time.Hour,
    })
    
    // Directory snapshot with no expiry
    snapshot, _ = sb.SnapshotDirectory(ctx, "/project", &modal.SandboxSnapshotDirectoryParams{
        TTL: modal.NoExpiryTTL,
    })

If you try to use an expired snapshot, Modal will raise a `NotFoundError` — immediately when mounting the Image into a running Sandbox, or upon first interaction (e.g. `exec` or `wait`) when starting a new Sandbox from the expired Image. Note that `Image.from_id()` is itself lazy and will not raise an error on construction even if the provided Image ID has been deleted.

To manage storage for long-lived snapshots, you can delete them programmatically when no longer needed. See [Deleting Snapshots](https://modal.com/docs/guide/sandbox-snapshots#deleting-snapshots)
 for details.

Filesystem Snapshots 

Filesystem Snapshots are copies of the Sandbox’s filesystem at a given point in time. These Snapshots are [Images](https://modal.com/docs/sdk/py/latest/modal.Image)
 and can be used to create new Sandboxes.

To create a Filesystem Snapshot, you can use the [`Sandbox.snapshot_filesystem()`](https://modal.com/docs/sdk/py/latest/modal.Sandbox#snapshot_filesystem)
 method:

    import modal
    
    app = modal.App.lookup("sandbox-fs-snapshot-test", create_if_missing=True)
    
    sb = modal.Sandbox.create(app=app)
    p = sb.exec("bash", "-c", "echo 'test' > /test")
    p.wait()
    assert p.returncode == 0, "failed to write to file"
    image = sb.snapshot_filesystem()
    sb.terminate()
    
    sb2 = modal.Sandbox.create(image=image, app=app)
    p2 = sb2.exec("bash", "-c", "cat /test")
    assert p2.stdout.read().strip() == "test"

Filesystem Snapshots are optimized for performance: they are calculated as the difference from your base image, so only modified files are stored. Restoring a Filesystem Snapshot utilizes the same infrastructure we use to get fast cold starts for your Sandboxes.

See [Snapshot Retention](https://modal.com/docs/guide/sandbox-snapshots#snapshot-retention)
 for TTL configuration options and [Deleting Snapshots](https://modal.com/docs/guide/sandbox-snapshots#deleting-snapshots)
 to learn how to manage snapshot storage.

Directory Snapshots 

Directory Snapshots allow you to snapshot a specific directory within a running Sandbox. The resulting snapshot is an Image that can then be mounted into another already-running Sandbox (typically at a later time), which can be useful for:

*   **Updating system dependencies separately from application code**: Base dependencies can be updated by starting a new Sandbox from an updated base Image, and then mounting in previously snapshotted application code.
*   **Using warm pools in combination with snapshots**: For use cases that benefit from a [warm pool](https://modal.com/docs/examples/sandbox_pool)
     of Sandboxes to reduce start-up latency, the first initialization can now happen in the warm pool without losing the ability to restore application-specific code at a later point in time.
*   **Speeding up resumptions of previous sessions**: Files in mounted Images are prioritized when containers load files, so mounting a directory can speed up Sandbox resumptions vs. starting from a full file system image.

### Usage 

Use `snapshot_directory` to snapshot a directory, `mount_image` to mount a previous directory snapshot at a directory path, and `unmount_image` to remove that mounted Image later. To protect directory snapshots with customer-held key material, see [Customer Supplied Encryption Keys](https://modal.com/docs/guide/customer-supplied-encryption-keys#directory-snapshots)
.

PythonJavaScriptGo

    sb = modal.Sandbox.create(app=app)
    # Write some dummy data
    sb.exec("bash", "-c", "mkdir /project && echo 'data' > /project/file.txt").wait()
    
    # Snapshot the directory
    snapshot = sb.snapshot_directory("/project")
    
    # Ok to throw away the old Sandbox at this point
    sb.terminate()
    
    # Mount the snapshot in a new Sandbox
    sb2 = modal.Sandbox.create(app=app)
    try:
        sb2.mount_image("/project", snapshot)
    except modal.exception.NotFoundError:
        # Handle a potential ttl expiry of the old snapshot here
        ...
    
    # The Sandbox now has access to the previous project state
    assert sb2.exec("cat", "/project/file.txt").stdout.read().strip() == "data"

    import { NotFoundError } from "modal";
    
    const sb = await modal.sandboxes.create(app, image);
    // Write some dummy data
    const p = await sb.exec([\
      "bash",\
      "-c",\
      "mkdir /project && echo 'data' > /project/file.txt",\
    ]);
    await p.wait();
    
    // Snapshot the directory
    const snapshot = await sb.snapshotDirectory("/project");
    
    // Ok to throw away the old Sandbox at this point
    await sb.terminate();
    sb.detach();
    
    // Mount the snapshot in a new Sandbox
    const sb2 = await modal.sandboxes.create(app, image);
    try {
      await sb2.mountImage("/project", snapshot);
    } catch (e) {
      if (e instanceof NotFoundError) {
        // Handle a potential ttl expiry of the old snapshot here
      }
    }
    
    // The Sandbox now has access to the previous project state
    const p2 = await sb2.exec(["cat", "/project/file.txt"]);
    console.assert((await p2.stdout.readText()).trim() === "data");
    sb2.detach();

    sb, _ := mc.Sandboxes.Create(ctx, app, image, nil)
    defer sb.Detach()
    
    // Write some dummy data
    p, _ := sb.Exec(ctx, []string{"bash", "-c", "mkdir /project && echo 'data' > /project/file.txt"}, nil)
    p.Wait(ctx, nil)
    
    // Snapshot the directory
    snapshot, _ := sb.SnapshotDirectory(ctx, "/project", nil)
    
    // Ok to throw away the old Sandbox at this point
    sb.Terminate(ctx, nil)
    
    // Mount the snapshot in a new Sandbox
    sb2, _ := mc.Sandboxes.Create(ctx, app, image, nil)
    defer sb2.Detach()
    
    if err := sb2.MountImage(ctx, "/project", snapshot, nil); err != nil {
      var notFound modal.NotFoundError
      if errors.As(err, ¬Found) {
        // Handle a potential ttl expiry of the old snapshot here
      }
    }
    
    // The Sandbox now has access to the previous project state
    p2, _ := sb2.Exec(ctx, []string{"cat", "/project/file.txt"}, nil)
    stdout, _ := io.ReadAll(p2.Stdout)
    fmt.Println(strings.TrimSpace(string(stdout))) // "data"

### Unmounting a mounted Image 

To unmount a previously mounted Image, call `unmount_image` on the exact path you passed to `mount_image`. After unmounting, the underlying Sandbox filesystem at that path becomes visible again.

PythonJavaScriptGo

    sb2.unmount_image("/project")

    await sb2.unmountImage("/project");

    _ = sb2.UnmountImage(ctx, "/project", nil)

Memory Snapshots 

Sandbox memory snapshots are copies of a Sandbox’s entire state, both in memory and on the filesystem. These Snapshots can be restored later to create a new Sandbox, which is an exact clone of the original Sandbox.

To snapshot a Sandbox, create it with `_experimental_enable_snapshot` set to `True`, and use the `_experimental_snapshot` method, which returns a `SandboxSnapshot` object:

    image = modal.Image.debian_slim().apt_install("curl", "procps")
    app = modal.App.lookup("sandbox-snapshot", create_if_missing=True)
    
    with modal.enable_output():
        sb = modal.Sandbox.create(
            "python3", "-m", "http.server", "8000",
            app=app, image=image, _experimental_enable_snapshot=True
        )
    
    print(f"Performing snapshot of {sb.object_id} ...")
    snapshot = sb._experimental_snapshot()

Create a new Sandbox from the returned SandboxSnapshot with `Sandbox._experimental_from_snapshot`:

    print(f"Restoring from snapshot {sb.object_id} ...")
    sb2 = modal.Sandbox._experimental_from_snapshot(snapshot)
    
    print("Let's see that the http.server is still running...")
    p = sb2.exec("ps", "aux")
    print(p.stdout.read())
    
    # Talk to snapshotted Sandbox http.server
    p = sb2.exec("curl", "http://localhost:8000/")
    reply = p.stdout.read()
    print(reply)  # <!DOCTYPE HTML><html lang...

The new Sandbox will be a duplicate of your original Sandbox. All running processes will still be running, in the same state as when they were snapshotted, and any changes made to the filesystem will be visible.

You can retrieve the ID of any Sandbox Snapshot with `snapshot.object_id` . To restore from a snapshot by ID, first rehydrate the Snapshot with `SandboxSnapshot.from_id` and then restore from it:

    snapshot_id = snapshot.object_id
    # ... save the Sandbox ID (sb-123abc) for later
    # sometime in the future...
    snapshot = modal.SandboxSnapshot.from_id(snapshot_id)
    sandbox = modal.Sandbox._experimental_from_snapshot(snapshot)

Note that these methods are _experimental_, and we may change them in the future.

### Re-snapshotting 

When creating a new memory snapshot from a Sandbox that was _itself_ created from a memory snapshot, the new snapshot inherits the expiration date of the original snapshot. This means a “chain” of snapshotted state can only ever become as old as the expiration date of the first snapshot in the series.

For example, snapshot_2 in the following example would only be valid for 3 days after creation:

    sandbox_1 = modal.Sandbox.create(_experimental_enable_snapshot=True)
    
    # snapshot_1 has a lifetime of 7 days from creation
    snapshot_1 = sandbox_1._experimental_snapshot()
    
    # 4 days later we do a restore + snapshot from snapshot_1
    print(f"Restoring from snapshot {snapshot_1.object_id} ...")
    sandbox_2 = modal.Sandbox._experimental_from_snapshot(snapshot_1)
    snapshot_2 = sandbox_2._experimental_snapshot()
    # snapshot_2 now has a lifetime of 7 - 4 = 3 days from creation

### Limitations 

*   Sandbox Memory Snapshots expire 7 days after creation (see [Snapshot Retention](https://modal.com/docs/guide/sandbox-snapshots#snapshot-retention)
    ). For longer persisting snapshots, try [Filesystem Snapshots](https://modal.com/docs/guide/sandbox-snapshots#filesystem-snapshots)
    .
*   Open TCP connections will be closed automatically when a Snapshot is taken, and will need to be reopened when the Snapshot is restored.
*   Snapshotting a Sandbox will currently cause it to terminate. We intend to remove this limitation soon.
*   Sandboxes created with `_experimental_enable_snapshot=True` or restored from Snapshots cannot run with GPUs.
*   It is not possible to snapshot a Sandbox while a `Sandbox.exec` command is still running. Furthermore, any background processes launched by a call to `Sandbox.exec` will not be properly restored after a snapshot.
*   Sandbox memory snapshots can only be restored on the same exact instance type that the original Sandbox was run on. Given Modal’s diverse fleet of capacity, this can sometimes lead to scheduling delays, especially when memory snapshots are combined with narrow region pinning.

Persisting Sandbox State 

To persist state across Sandbox sessions, you need to:

1.  **Trigger the snapshot.** Snapshots are triggered from outside the Sandbox, typically just before termination. A common pattern is to run an exec process inside the Sandbox and wait for it to exit. Once it does, the controller takes a snapshot and terminates the Sandbox.
2.  **Store the snapshot ID.** The `object_id` string must be persisted so you can restore from it later. This is typically keyed by a session or user ID, and can be stored in your database, an external key-value store, or a [Modal Dict](https://modal.com/docs/guide/dicts)
    .

The following example shows this pattern. This code would typically run in a Modal Function or your own backend, orchestrating the Sandbox:

    import modal
    
    app = modal.App.lookup("sandbox-snapshot-lifecycle", create_if_missing=True)
    snapshot_store = modal.Dict.from_name("sandbox-snapshots", create_if_missing=True)
    session_id = "sess_a1b2c3d4"
    
    # Restore from snapshot, or use base image
    if session_id in snapshot_store:
        image = modal.Image.from_id(snapshot_store[session_id])
    else:
        image = modal.Image.debian_slim()
    
    sb = modal.Sandbox.create(image=image, app=app)
    
    # Run agent which exits when ready to be snapshotted
    p = sb.exec("python", "agent.py")
    p.wait()
    
    # Snapshot and store the object_id
    snapshot_store[session_id] = sb.snapshot_filesystem().object_id
    sb.terminate()

Deleting Snapshots 

Since both Filesystem and Directory Snapshots are [Images](https://modal.com/docs/sdk/py/latest/modal.Image)
, you can delete them using the image deletion API. This is useful for managing storage or complying with data retention policies.

PythonJavaScriptGo

    import modal.experimental
    
    # Get the image ID from a filesystem or directory snapshot
    image = sb.snapshot_filesystem()
    # or: image = sb.snapshot_directory("/project")
    image_id = image.object_id  # e.g., "im-abc123"
    
    # Later, delete the snapshot when no longer needed
    modal.experimental.image_delete(image_id)

    // Get the image ID from a filesystem or directory snapshot
    const image = await sb.snapshotFilesystem();
    // or: const image = await sb.snapshotDirectory("/project");
    const imageId = image.imageId; // e.g., "im-abc123"
    
    // Later, delete the snapshot when no longer needed
    await modal.images.delete(imageId);

    // Get the image ID from a filesystem or directory snapshot
    image, _ := sb.SnapshotFilesystem(ctx, nil)
    // or: image, _ := sb.SnapshotDirectory(ctx, "/project", nil)
    imageId := image.ImageID // e.g., "im-abc123"
    
    // Later, delete the snapshot when no longer needed
    mc.Images.Delete(ctx, imageId, nil)

To delete snapshots, you need to track the image IDs yourself (e.g., in a database or [Modal Dict](https://modal.com/docs/guide/dicts)
), since there is currently no API to list all snapshots you have created.

[Snapshots](https://modal.com/docs/guide/sandbox-snapshots#snapshots)
[Snapshot Retention](https://modal.com/docs/guide/sandbox-snapshots#snapshot-retention)
[Filesystem Snapshots](https://modal.com/docs/guide/sandbox-snapshots#filesystem-snapshots)
[Directory Snapshots](https://modal.com/docs/guide/sandbox-snapshots#directory-snapshots)
[Usage](https://modal.com/docs/guide/sandbox-snapshots#usage)
[Unmounting a mounted Image](https://modal.com/docs/guide/sandbox-snapshots#unmounting-a-mounted-image)
[Memory Snapshots](https://modal.com/docs/guide/sandbox-snapshots#memory-snapshots)
[Re-snapshotting](https://modal.com/docs/guide/sandbox-snapshots#re-snapshotting)
[Limitations](https://modal.com/docs/guide/sandbox-snapshots#limitations)
[Persisting Sandbox State](https://modal.com/docs/guide/sandbox-snapshots#persisting-sandbox-state)
[Deleting Snapshots](https://modal.com/docs/guide/sandbox-snapshots#deleting-snapshots)
