> ## Documentation Index
> Fetch the complete documentation index at: https://docs.beam.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Durable Disks

> Fast, node-local persistent storage for stateful workloads

Durable disks give a container its own persistent directory on the local SSD of the machine it runs on. Reads and writes go straight to local storage, and the contents survive across container restarts and redeployments.

Use a durable disk when one process owns the data and needs fast, POSIX-complete access to it: a Postgres or Redis instance, a SQLite database, a vector index, a build cache, or a message queue.

<Tip>
  If several containers need to read and write the same files at the same time, use a [Volume](/v2/data/volume) instead. Volumes are shared, distributed storage. Disks are attached to one running container at a time.
</Tip>

## Choosing Between Storage Types

|                           | Volumes                                                           | Cloud Buckets                       | Durable Disks                                               |
| ------------------------- | ----------------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------- |
| Backed by                 | Beam's distributed file system                                    | Your S3-compatible bucket           | Local SSD on the worker node, snapshotted to object storage |
| Shared between containers | Yes                                                               | Yes                                 | One writer at a time (any number of read-only mounts)       |
| Best for                  | Model weights, datasets, shared outputs                           | Data you already keep in S3         | Databases, caches, and other single-writer state            |
| Consistency               | Writes become visible to other containers within about 60 seconds | Depends on the provider; no appends | Local disk semantics inside the container                   |

## Attaching a Disk

Declare a `DurableDisk` with a name, a size, and the path where it should appear inside the container, then pass it to your app with the `disks` parameter.

```python theme={null}
from beam import DurableDisk, function

disk = DurableDisk(name="app-data", size="10Gi", mount_path="/data")


@function(cpu=0.25, memory=128, disks=[disk])
def append_and_read():
    with open("/data/runs.log", "a") as f:
        f.write("container ran\n")

    with open("/data/runs.log") as f:
        return f.read()


if __name__ == "__main__":
    print(append_and_read.remote())
```

Run it, wait for the container to exit, and run it again. The second run returns two lines: the first one was written by a container that no longer exists.

The `disks` parameter is accepted by [`Pod`](/v2/pod/web-service), [`function`](/v2/function/running-functions), [`endpoint`](/v2/endpoint/overview), [`task_queue`](/v2/task-queue/running-tasks), and [`Sandbox`](/v2/sandbox/overview).

If a disk with that name does not exist in your workspace yet, Beam creates it the first time you run or deploy the app. You can also create disks ahead of time from the [CLI](#cli-management-commands).

### Running a Database on a Disk

Disks are a natural fit for `Pod`, which can run any container image as a long-lived service. This example runs Redis with its append-only file stored on a durable disk:

```python theme={null}
from beam import DurableDisk, Image, Pod

redis = Pod(
    name="redis",
    image=Image(base_image="redis:7"),
    ports=[6379],
    tcp=True,
    keep_warm_seconds=-1,
    disks=[DurableDisk(name="redis-data", size="5Gi", mount_path="/data")],
    entrypoint=["redis-server", "--appendonly", "yes"],
)
```

```sh theme={null}
beam deploy app.py:redis
```

When you redeploy, the new container starts with the same `/data` directory the previous one wrote to.

### Parameters

<ParamField body="name" type="string" required>
  The name of the disk. Names are unique within your workspace, so two apps that declare the same name share the same disk.
</ParamField>

<ParamField body="size" type="string" required>
  The declared size of the disk, for example `"10Gi"` or `"500Mi"`. The size is recorded on the disk and shown in the CLI; it is not changed by redeclaring the disk with a different value.
</ParamField>

<ParamField body="mount_path" type="string" required>
  The absolute path where the disk is mounted inside the container. A disk without a mount path is not attached.
</ParamField>

<ParamField body="filesystem" type="string" default="ext4">
  Recorded on the disk for reference.
</ParamField>

<ParamField body="read_only" type="boolean" default="False">
  Mount the disk read-only. Read-only disks can be attached to any number of containers at once. See [Read-Only Disks](#read-only-disks).
</ParamField>

## Snapshots

Beam snapshots a disk to object storage each time the container using it stops. Only data that changed since the previous snapshot is uploaded. Snapshots are not taken on a timer: while a container is running, its writes live on the node's local disk, and changes made since the container last stopped are not yet in a snapshot.

When a container starts, Beam reuses the disk's local copy if the container lands on the node that ran it last, and otherwise restores the latest snapshot onto the new node. For a large disk the restore is the slower path, so long-running services with `keep_warm_seconds=-1` avoid it entirely.

You can list the snapshots of a disk with the CLI:

```sh theme={null}
beam disk snapshots redis-data
```

```
  Disk Name    Status      Format          Generation            Logical Size   Stored Size   Created At
 ───────────────────────────────────────────────────────────────────────────────────────────────────────────
  redis-data   available   redis.aof.v1    1783980128619331915   234.00 B       234.00 B      Jul 13 2026
  redis-data   available   redis.aof.v1    1783979768654042369   340.00 B       340.00 B      Jul 13 2026

  2 snapshots
```

* **Generation** is a timestamp assigned when the snapshot is taken. The highest generation is the most recent snapshot and is the one used for restores.
* **Logical Size** is the total size of the files in the snapshot. **Stored Size** is the size of the object storage chunks the snapshot references.
* **Format** describes how the directory was captured. `dir.v1` is the general format. Beam's managed databases use `postgres.wal.v1` and `redis.aof.v1`, which recognize Postgres write-ahead log segments and Redis append-only files: when one of those files has grown since the last snapshot, only the newly appended tail is uploaded.

Snapshots are kept until you delete them. Deleting a disk does not delete its snapshots.

## Read-Only Disks

Only one container can have a writable mount of a given disk at a time. Deploying an app that mounts a writable disk with an autoscaler that allows more than one container is rejected:

```
✗ writable durable disks support one container; set max containers to 1 or mark the disk read_only
```

Set `read_only=True` to mount a disk without write access. Read-only mounts are exempt from the single-writer rule, so you can attach the same disk to a horizontally scaled endpoint. A typical pattern is one writer that prepares data, such as a nightly job that builds an index, and many readers that serve it:

```python theme={null}
from beam import DurableDisk, QueueDepthAutoscaler, endpoint

index = DurableDisk(name="search-index", size="20Gi", mount_path="/index", read_only=True)


@endpoint(
    name="search",
    disks=[index],
    autoscaler=QueueDepthAutoscaler(max_containers=10),
)
def search(query: str):
    ...
```

Readers start from the disk's latest snapshot, so a new snapshot only reaches them the next time their containers start.

## Limits and Caveats

* **One writer.** A writable disk can be mounted by one container at a time. Scale-out requires `read_only=True`.
* **Durability is per container lifetime.** Data is copied to object storage when the container stops. Changes made during a run are on the node's local disk until then.
* **Size is fixed at creation.** Declaring an existing disk with a different `size` returns the existing disk unchanged. The size is not enforced as a quota, and `df` inside the container reports the capacity of the node rather than the declared size.
* **Deleting is soft.** `beam disk delete` removes the disk from your workspace but leaves its snapshots in place. Apps that still reference the name will recreate the disk record and, on their next start, restore from the latest snapshot.
* **Mount path is required.** A `DurableDisk` without a `mount_path` is ignored.
* **Leave the marker file alone.** Every disk contains a small `.beta9-durable-disk` file at its root that Beam uses to track snapshot state.

## Managed Databases

Beam's managed Postgres and Redis services are built on durable disks. `beam db postgres create <name>` deploys a Postgres container with a `<name>-data` disk mounted at `/var/lib/postgresql/data`, and `beam db redis create <name>` does the same with a disk at `/data`. The disks show up in `beam disk list` alongside any you create yourself.

```sh theme={null}
beam db postgres create mydb
beam db postgres connect mydb --psql
```

## CLI Management Commands

### List Disks

```sh theme={null}
beam disk list
```

```
  Name          Size   Filesystem   Mount Path   Created At    Workspace Name
 ───────────────────────────────────────────────────────────────────────────────
  redis-data    5Gi    ext4         /data        Jul 13 2026   85cfa7
  app-data      10Gi   ext4         /data        Sep 13 2026   85cfa7

  2 disks
```

Add `--format json` to get the full record, including the disk `id` and `driver`.

### Create a Disk

```sh theme={null}
beam disk create [DISK-NAME] --size 10Gi --mount-path /data
```

`--size` defaults to `10Gi`. `--mount-path` sets the disk's default mount path, which is shown in `beam disk list`; the `mount_path` you set in code is what determines where the disk is mounted. If a disk with that name already exists, the command returns it unchanged.

### List Snapshots

```sh theme={null}
beam disk snapshots [DISK-NAME]
```

Omit the name to list snapshots for every disk in the workspace.

### Delete a Disk

```sh theme={null}
beam disk delete [DISK-NAME]
```

```
Any apps or services (functions, endpoints, databases, etc) that
refer to this disk should be updated before it is deleted.
Are you sure? [y/N]: y

✓ Deleted disk: app-data
```

Pass `-y` to skip the confirmation.
