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

# Storage and backups

> Choose local, SQL or MongoDB storage and back up the complete replay archive.

# Storage and backups

Use local storage to get started. Use shared SQL or MongoDB storage when recordings need to be available on several machines.

> [!NOTE]
>
> **Not sure which one to pick?** Stay with the default (`spatial`, local files) until you actually run more than one server. You can switch later — see [Deletion and migration](#deletion-and-migration).

## Choose a backend

| Backend | Good fit | Important property |
| - | - | - |
| `spatial` | One-machine use and local development | Replay files and content-addressed terrain live on disk. |
| `sql` with MariaDB/MySQL/PostgreSQL | Multiple gameplay/lobby/replay machines | Metadata, event payloads, and captured terrain can be shared through SQL. |
| `sql` with SQLite | A local SQL-backed installation | A local database file is not a multi-machine service. |
| `mongodb` | Shared MongoDB 6.0+ deployments | Catalog metadata, GridFS replay bodies, remote terrain and database availability share one database. |

Set the case-sensitive id in `storage.backend`. The built-in ids are `spatial`, `sql` and `mongodb`; an unknown id fails startup with the list of known ids. Startup resolves a fixed set of factories shipped with Reverse. It does not discover backend plugins or scan Bukkit services for storage factories.

Your storage choice applies when the server starts and is used for both recordings and captured terrain. The folder itself is set by `storage.recordings-folder`: empty uses `plugins/recordings`, a relative path resolves against the plugin data folder's parent, and an absolute path is used as-is.

For the standard `plugins/reverse-recorder` data folder:

| `recordings-folder` | Resolved location |
| - | - |
| `''` or whitespace only | `plugins/recordings` |
| `match-archive` | `plugins/match-archive` |
| An absolute path | That path, used verbatim |

Configure the same resolved folder for recorder and standalone player on one machine. Folder changes require a restart and do not automatically copy, migrate, or import the old dataset.

> [!NOTE]
>
> **Redis is not replay storage.** It can coordinate lobby transfers, but events, metadata, and captured terrain still need local, SQL or MongoDB storage.

With the default local storage settings, recordings are kept in `plugins/recordings`:

```text theme={null}
plugins/
  reverse-recorder/
  reverse-player/
  reverse-lobby/
  recordings/
    <replay-uuid>.rpl
    .replay.lock
    .spatial.lock
    manifests/
    chunks/
      index.dat
      packs/
        pack-000000.dat
        ...
```

Some terrain-index paths use encoded subdirectories rather than a simple visible template name. Treat the entire storage tree as one managed dataset.

The `.rpl` files hold recorded actions, while the other recording folders hold the terrain they need. Keep the whole `recordings` folder together. Do not remove individual terrain files because they appear to belong to an old replay; several recordings may share them.

For several machines, use shared SQL or MongoDB storage rather than treating a network-mounted folder as a database.

## Sharing local storage between roles

Recorder and player can use the same spatial archive on one machine, even though they run as independent plugins. A player that was started before a new recording can discover its saved terrain without restarting.

Spatial writes use the permanent `.spatial.lock` file to coordinate access across plugin instances and processes. Each successful chunk write publishes its pack data and CAS index before completing. Readers refresh the index before reporting a chunk missing, and closing an idle reader cannot replace a newer index with its old copy.

Keep `.spatial.lock` and `.replay.lock` in place while Reverse is running. They are managed coordination files, not stale recording data. Spatial storage requires filesystem locking and atomic file moves; use shared SQL or MongoDB for servers on different machines.

When opening an archive whose CAS index is missing or behind its pack files, Reverse can rebuild the index from verified complete payloads. Corrupt or truncated packs fail explicitly; preserve the complete archive and logs for diagnosis rather than deleting individual files.

## SQL configuration

Set `storage.backend: sql` and configure the nested database section. Port defaults in the generated file are MySQL-oriented, so use 5432 explicitly for PostgreSQL.

The configured account needs permission to create Reverse's tables and read/write its data. Back up before changing storage or updating the plugin, and avoid editing recording data directly in database tables.

SQLite resolves `storage.database.sqlite-file` against the selected recordings folder, not the role's own configuration folder. The defaults therefore select `plugins/recordings/replay.db`; changing `recordings-folder` also changes the base for a relative SQLite filename. An absolute SQLite path is used as-is. SQLite uses a single-connection pool. For a remote database, connection limits are configured under `storage.database.pool`.

The database backend downloads the common SQL libraries and only the selected dialect's driver group. MySQL and MariaDB use the MariaDB driver; PostgreSQL and SQLite have their own drivers. You do not need to install a separate JDBC plugin. A successful SQL startup does not create a spatial terrain provider alongside SQL.

## MongoDB configuration

Use the same URI and database on recorder, player and lobby:

```yaml theme={null}
storage:
  backend: mongodb
  mongodb:
    uri: 'mongodb://localhost:27017'
    database: reverse
    workers: 4
    timeout-millis: 5000
  fallback-to-spatial: false
```

MongoDB **6.0 or newer** is required. Replace the URI with your MongoDB endpoint and credentials
when needed. The account must be able to create collections and indexes and read/write the
database, including GridFS buckets. The driver is downloaded by Libby only when MongoDB is
selected; no separate MongoDB plugin is needed.

Replay bodies use `replay_payloads` GridFS and terrain uses `replay_terrain` GridFS, so replay
bodies and world indexes can exceed MongoDB's 16-MB document limit. Catalog queries read only
metadata. Renaming and descriptions do not download or rewrite the recorded events.

For cross-server playback, `network.backend: mongodb` uses the selected MongoDB backend's
availability collections. Redis remains an optional alternative. A replica set is not required
for the storage publication scheme.

## Fallback behavior

`fallback-to-spatial: true` allows a startup failure while loading/configuring the SQL or MongoDB backend to select local storage instead. This includes more than an unreachable endpoint. Fallback switches both replays and terrain and uses the same resolved recordings folder. SQL logs:

```text theme={null}
Storage backend 'sql' unavailable; using 'spatial'.
```

MongoDB logs `Storage backend 'mongodb' unavailable; using 'spatial'.` This is not replication, automatic re-upload, or a live switch back to the remote database. Repair the database and restart deliberately. Recordings created locally during fallback remain local unless you perform a separately planned transfer.

For shared deployments, `false` is usually easier to operate: a node fails clearly instead of serving a different private catalog.

## Catalog versus full replay loading

Browsing the catalog does not load every recording for playback. Renaming a replay or editing its description changes its catalog text, not the recorded scene. Reopen the catalog to see updated details.

## Cache sizing

The shared configuration exposes maximum chunks, terrain indices (historically named manifests), and loaded replays. They are count limits, not megabytes. A loaded replay can be much larger than its compressed `.rpl` file.

Leave cache limits at their defaults unless you have a reason to tune them. Watch server memory use when increasing them. Cache limits do not delete saved recordings.

## Back up correctly

> [!WARNING]
>
> **Back up events and terrain together.** Copying only `.rpl` files can leave a recording without the world it needs to display.

### Local storage

1. Stop new recordings.
2. Wait for pending saves, then stop the affected servers cleanly.
3. Copy the **entire** configured recordings tree (`plugins/recordings` by default), including replay files, indices, and packs.
4. Back up any SWM templates and their configured loader data separately.
5. Restore a test copy and replay a known recording before relying on the backup.

### SQL storage

Use the database engine's consistent backup method for the entire Reverse dataset. Include all replay metadata/event tables and terrain data/index tables from the same consistent snapshot. Back up external SWM templates separately.

### Redis

Availability data is short-lived coordination state, not the replay archive. A Redis snapshot alone cannot restore recordings. Stale heartbeats and expired pending selections are normal control-plane concerns, not lost replay payloads.

### MongoDB storage

Back up the full Reverse database consistently, including `replay_sessions`, `replay_cas_chunks`,
`replay_world_indexes` and both GridFS buckets' `.files` and `.chunks` collections. Stop writers
for a standalone backup or use MongoDB's consistent-backup procedures. Back up external SWM
templates separately.

Deleting a replay removes its authoritative catalog document. Old replay generations and terrain
blobs remain stored so a load already in progress on another server can finish safely. Storage
usage is therefore not reclaimed immediately. Perform any orphan cleanup offline with all
Reverse readers/writers stopped; there is no automatic online garbage-collection task.

## Deletion and migration

Use the catalog's authorized delete operation or a configured menu delete action for replay deletion. Do not assume deletion compacts terrain packs or provides a configurable retention schedule. No owner-facing automatic retention/garbage-collection command is documented by the current command surface.

Changing between `spatial`, `sql` and `mongodb` does not import existing recordings. There is currently no general-purpose migration command. Keep the original backup and plan migration using supported storage APIs/tools for the exact build.

The same applies when changing `recordings-folder` or the SQLite filename: the next startup opens the newly selected location, without moving data from the previous one.

## Related guides

* [Exact storage configuration keys](https://docs.marioded.com/owners/configuration.md)
* [Connect the same archive to lobby and player](https://docs.marioded.com/owners/network.md)
* [Understand captured terrain and templates](https://docs.marioded.com/owners/terrain.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.