# Volumes

A volume is persistent disk storage mounted into your app at a path you choose, such as `/data`. Files written there survive restarts and deploys. Use one for uploads, an embedded [SQLite database](sqlite.md), or any app that keeps its data on local disk.

## Adding a volume

On the app's **Resources** tab, choose **Add dependency → Volume**:

- **Create new**: set the **Volume name**, **Capacity (GB)** (20 by default) and **Mount path** (`/data` by default), and optionally **Read only**.
- **Attach existing**: pick a volume your organization already has and set its mount path.

The app restarts with the volume mounted. Adding a SQLite database creates its volume for you.

From the CLI:

```sh
nimbus volumes create --name uploads --size 20
nimbus volumes attach --volume <volume-id> --app <app-id> --path /data
nimbus volumes list
```

The HTTP API offers the same through `POST /api/v1/volumes` and `POST /api/v1/volumes/:id/attachments`.

### What to know

- An app with a volume stops its old instance before starting the new one on each deploy, so there is a short gap instead of a rolling update.
- Volumes are mounted in production only. Staging and preview environments run without them.
- Volumes are not available to static sites.

## What happens to the data

| Action | The data |
|---|---|
| **Detach** | Kept. The volume shows as kept from the app it came from, and can be attached again to that app or another app in your organization. |
| **Attach** a kept volume | Moves with it, to the same app or another one. A volume that has data never gets empty storage in its place. |
| **Delete the volume** | Destroyed. Its snapshots are kept for 7 days. |
| **Delete the app** | Volumes still attached are destroyed, with all their snapshots. Volumes you detached earlier are kept. |

If a step fails, the volume stays as it was and its entry shows the reason; try the action again. Deleting a volume, or an app with volumes attached, asks you to type its name first. Detaching has a one-step confirm, because nothing is lost.

To keep a volume's data before deleting an app, detach the volume first.

## Snapshots

Every attached volume gets point-in-time snapshots: automatic daily ones, plus manual ones you take whenever you like. Manage them in the **Snapshots** section of the volume's Edit dialog on the Resources tab.

A snapshot is crash-consistent: it holds exactly what a sudden power loss would have left on the volume at that moment. Software that survives a power cut, such as SQLite, Postgres or files written in one go, restores cleanly. Taking a snapshot never pauses or restarts your app.

Snapshots are kept on the same storage as the volume. They protect against deletion and mistakes (an app deleting its own files, a migration gone wrong, a volume deleted by accident), not against losing that storage. They are not an off-site copy. Keep your own copy of anything irreplaceable.

### Schedule and retention

- **Automatic daily snapshots** are on by default and run once a day after 03:30 UTC. You can switch them off per volume; existing snapshots stay.
- **Automatic snapshots kept**: the newest 7 by default, settable from 1 to 30. The oldest is removed only once a newer one is ready. Lowering the number below what is kept now deletes the oldest ones, so you are asked to type the volume's name first.
- **Manual snapshots**: choose **Take snapshot**, with an optional label of up to 80 characters. A volume can keep up to 10. They don't count toward the automatic retention and stay until you delete them.
- **Failed snapshots** show the reason, are never offered for restore, and are removed after a day. A failed automatic snapshot is retried an hour later, up to three times a day.

The section shows how many snapshots the volume has and an approximate size, which overstates the real space because snapshots share unchanged data with the volume.

### Restoring a snapshot

Restoring never overwrites the volume the snapshot came from. It creates a **new volume** of the same size, named after the volume and the snapshot time. The restore dialog offers to attach it now in place of the current volume at the same mount path: the current volume is detached and kept, the new one attached, and the app restarts once. Untick that option to attach the new volume later from **Add dependency → Volume → Attach existing**.

Until it is first attached, a restored volume can only be attached to the app its snapshot came from.

### After deleting a volume

A deleted volume's snapshots are kept for 7 days. During that time the volume is listed under **Recently deleted** in **Add dependency → Volume**, with the days left and a **Restore** action for each snapshot. After 7 days the snapshots are removed.

Snapshots don't survive deleting the whole app: every snapshot of every volume the app has goes with it, and the delete dialog says how many.

### Snapshots from the API

| Request | What it does |
|---|---|
| `GET /api/v1/volumes/:id/snapshots` | The volume's snapshots, newest first |
| `POST /api/v1/volumes/:id/snapshots` | Take a manual snapshot; optional body `{"label": "before schema change"}` |
| `PUT /api/v1/volumes/:id/snapshot-policy` | Body `{"automatic_snapshots": true, "snapshot_retention": 14}`; lowering retention below the snapshots kept now also needs `"confirm": "<volume name>"` |
| `DELETE /api/v1/volume-snapshots/:id` | Delete a snapshot |
| `POST /api/v1/volume-snapshots/:id/restore` | Restore into a new volume; body `{"attach": "in_place"}` or `{"attach": "none"}` (the default) |
| `GET /api/v1/apps/:id/volume-snapshots` | Every snapshot of the app's volumes, including those of volumes deleted in the last 7 days |

Reading snapshots needs the **View storage** permission; changing them needs **Manage storage**, plus **Manage apps** to attach a restored volume in place.
