---
id: platform/persistent-disks
title: Persistent disks
description: Attach persistent storage to a Bex service, grow it, inspect snapshots, and understand restore and deletion risks.
keywords: [bex, persistent disk, storage, volume, snapshots, backup]
last_updated: 2026-09-23
---

A persistent disk preserves files written under its mount path across service
restarts and deployments. The rest of the container filesystem remains
disposable. Use a disk when your application needs local persistent files;
use [PostgreSQL](./postgres.md) or [Key Value](./key-value.md) when you need
those managed database capabilities.

## Requirements and tradeoffs

- One disk per paid web service, private service, or background worker.
- No disks on free services, cron jobs, or static sites.
- A disk-backed service runs at most one instance and cannot use autoscaling.
- Deployments stop the old instance before starting its replacement. Plan for
  an interruption; this service no longer uses rolling updates.
- The disk is available to the running service, not its build or pre-deploy
  command. It is not a shared filesystem for other services.
- Bex's current API accepts sizes from **10 to 10,000 GB**, defaulting to
  10 GB. Storage can grow but cannot shrink.

Self-hosted installations need a working storage provisioner and control-plane
service management. Snapshot and restore support additionally requires configured
backup storage and encryption keys. A successful disk attachment does not prove
that backups are configured or succeeding.

## Attach a disk

In the service's **Disks** page, provide a name, absolute mount path, and size.
Choose a path used only for data, such as `/var/data`, and configure your
application to write there. Mounting a volume hides files already present at
that path in the image; it does not copy those files into the volume.

You can also declare a disk in a [`render.yaml` Blueprint](./app-resource.md):

```yaml
services:
  - name: uploads-api
    type: web
    runtime: docker
    repo: https://github.com/your-org/uploads-api
    plan: starter
    numInstances: 1
    disk:
      name: uploads
      mountPath: /var/data
      sizeGB: 10
```

Replace the repository and choose an available paid plan. Validate and apply
through the Blueprint workflow. For an existing eligible service, the API
supports `POST /v1/disks` with `serviceId`, `name`, `mountPath`, and `sizeGB`.

The mount path must be absolute and cannot replace `/` or reserved platform
paths such as `/etc`, `/etc/secrets`, or `/opt/render/project/src`. Prefer a
separate data directory instead of mounting over your application or secrets.

Wait for the disk and service to become ready. Write a noncritical test file
through the application, restart the service, and verify that the file remains
before relying on the volume for production data.

## Inspect and grow storage

Set `BEX_API_URL` to your API origin, `BEX_TOKEN` to an authorized token,
`SERVICE_ID` to the owning service, and `DISK_ID` to the returned disk id.
To find the service's disk:

```bash
curl --fail-with-body "$BEX_API_URL/v1/disks?serviceId=$SERVICE_ID" \
  -H "Authorization: Bearer $BEX_TOKEN"
```

To increase a 10 GB disk to 20 GB:

```bash
curl --fail-with-body -X PATCH "$BEX_API_URL/v1/disks/$DISK_ID" \
  -H "Authorization: Bearer $BEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sizeGB":20}'
```

A successful response records the requested size. Provisioner expansion and
filesystem growth can finish later; a restart may be needed to finish filesystem
expansion. Check capacity before writing into the additional space. Do not
assume Render's documented online-growth timing applies to Bex's storage driver.

Changing the mount path changes where the same volume appears to the process.
Update the application's data-path configuration with it. Increasing capacity
does not relocate or clean up existing files.

## Check snapshots before you need them

When configured, Bex schedules daily encrypted file-level snapshots into object
storage and retains the seven newest snapshots. With successful daily backups,
that gives approximately a week of restore points. These are filesystem archives, not
application-consistent database backups. Inspect the available snapshots and
practice recovery; do not infer a successful backup from the schedule alone.

```bash
curl --fail-with-body "$BEX_API_URL/v1/disks/$DISK_ID/snapshots" \
  -H "Authorization: Bearer $BEX_TOKEN"
```

If the API returns `DISK_SNAPSHOTS_NOT_CONFIGURED`, this installation has not
configured snapshot access. Ask the instance operator to configure it and
verify a completed backup. Each returned `snapshotKey` expires after 24 hours;
list again when you need a fresh restore key.

## Restore a snapshot

Restoring replaces the disk's contents: **writes made after the selected
snapshot are lost**, and the service is stopped for the restore. Export anything
you need to retain before proceeding. For a database you manage on a service
disk, use database-native backup and recovery tools instead of treating a
filesystem snapshot as a consistent database backup.

Select the snapshot in the dashboard and review the destructive-action prompt.
API clients use `POST /v1/disks/{diskId}/snapshots/restore` with a JSON body
containing `snapshotKey` from the current list.

The API returns the disk after accepting the request; that response does not
mean recovery is complete. Follow the service and restore status, wait for
readiness, then verify both files and application behavior before resuming users
or background processing.

## Delete a disk

Disk deletion destroys its data and removes access from the service. Export a
backup you control first; do not rely on the disk's own snapshots surviving its
removal. Use the dashboard deletion control or `DELETE /v1/disks/{diskId}` only
after checking the owning service and id. Suspension is a different action and
does not delete storage.

## Troubleshoot

Check the service's disk status and events. `StorageBlockedByQuota` indicates
insufficient storage quota; `StorageClassNotExpandable` indicates a provisioner
that cannot perform the requested growth; `DiskResizePending` can mean filesystem
expansion is waiting to complete. For permission errors, check the application's
runtime user and the mount directory. For disappearing files, confirm the
application actually wrote under the mounted path.

See [Scaling](./scaling.md), [How deploys work](./how-deploys-work.md), and
[App lifecycle](./app-lifecycle.md) for the operational effects of a disk.
Platform etcd, OpenBao, Postgres archives, and Key Value RDB drills are a
separate model — see [Platform recovery](./recovery.md).
