Backup and Restore¶
A Record Store deployment has two things worth backing up, plus one thing that is not in either.
| Where | How | |
|---|---|---|
| Metadata | <data_directory>/metadata/ |
record-store server backup-metadata |
| Object payloads | <data_directory>/objects/ |
Your normal file backup |
| The credential master key | Your secret manager | Manually, once |
The master key is not in any backup
RECORD_STORE_CREDENTIAL_MASTER_KEY seals credentials, webhook secrets, and — with
encryption on — every object. A backup without it is unusable.
It is deliberately not stored in the data directory: keeping it there would make one stolen backup a complete compromise. Back it up separately, once, and record where it lives in your runbook.
Metadata and payloads are one unit¶
Object payloads are meaningless without the catalog that names them, and catalog entries pointing at absent payloads are data loss.
Back them up together, and restore them together, from the same point in time. Mixing a Tuesday catalog with a Thursday payload directory produces both dangling references and orphans.
Backing up metadata¶
What it does:
- Takes the exclusive data lock, so nothing writes while it runs
- Copies every
.redbfile frommetadata/ - Records a SHA-256 checksum and size per file in
manifest.json - Stamps the backup format version and the metadata schema version
The output directory must not already exist. Each run needs a fresh destination — that is what stops a partial overwrite of a good backup.
The server must be stopped
The command takes the exclusive data lock. It fails with a "data directory in use" error if the server is running.
A filesystem snapshot of a running deployment can catch metadata mid-write. Use this command rather than trusting a live snapshot.
Backing up payloads¶
Object payloads are immutable once committed, so an incremental copy is safe:
Do not back up tmp/ — it holds incomplete uploads and is disposable.
A backup script¶
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%Y-%m-%d)
DEST="/backups/${STAMP}"
DATA="/var/lib/record-store"
docker stop --time 40 record-store
record-store server backup-metadata \
--config /etc/record-store/config.toml \
--output "${DEST}/metadata"
rsync -a "${DATA}/objects/" "${DEST}/objects/"
docker start record-store
echo "backup complete: ${DEST}"
The downtime is the length of the metadata copy plus the rsync delta. For a deployment that cannot take that, snapshot the whole volume with a filesystem that does atomic snapshots (ZFS, LVM) and take the metadata backup from the snapshot.
Restoring metadata¶
# 1. Stop the server
docker stop --time 40 record-store
# 2. The metadata directory must be empty
mv /var/lib/record-store/metadata /var/lib/record-store/metadata.old
# 3. Restore
record-store server restore-metadata /backups/2026-08-29/metadata
# 4. Restore payloads from the same backup
rsync -a /backups/2026-08-29/objects/ /var/lib/record-store/objects/
# 5. Start
docker start record-store
# 6. Verify
record-store storage inspect --endpoint http://127.0.0.1:7601
Restore refuses to proceed unless:
- The
metadata/directory is empty - Every file's size and SHA-256 match the manifest
- The backup format version matches
- The metadata schema version is not newer than the running binary
Move the old directory aside rather than deleting it. If the restore turns out to be the wrong one, you still have what you had.
Files are staged into a temporary directory and renamed into place only after every checksum verifies, so a failed restore leaves nothing half-applied.
Testing restores¶
An untested backup is a belief, not a backup.
# On a scratch machine, into an empty data directory
RECORD_STORE_STORAGE_DATA_DIRECTORY=/tmp/restore-test \
RECORD_STORE_ROOT_ACCESS_KEY=<your-access-key> \
RECORD_STORE_ROOT_SECRET_KEY=<your-secret-key> \
RECORD_STORE_CREDENTIAL_MASTER_KEY=<the same master key> \
record-store server restore-metadata /backups/2026-08-29/metadata
rsync -a /backups/2026-08-29/objects/ /tmp/restore-test/objects/
# Start it and confirm the data is really there
record-store storage inspect --endpoint http://127.0.0.1:7601
record-store verify bucket uploads --endpoint http://127.0.0.1:7601
Use the same master key. A restore with a different one produces a deployment whose credentials are unreadable — which is exactly the failure a test should catch.
Test quarterly, and after any upgrade that changed the metadata schema.
Retention¶
A workable schedule:
| Frequency | Keep |
|---|---|
| Daily | 7 days |
| Weekly | 4 weeks |
| Monthly | 12 months |
Store at least one copy off the machine that runs Record Store, and encrypt or access-control the backup location — it contains everything.
The audit trail is inside the metadata backup and is never pruned, so backups grow with request volume as well as with data. See Capacity Planning.