Backup & Restore
VeltrixDB backs up the write-ahead log and Value Log directly, supports full and incremental backup chains, uploads to S3/GCS/Azure, and restores by replaying the chain back onto a fresh data directory.
What gets backed up
VeltrixDB stores two files per disk: wal.log (a compacted write-ahead log — one entry per live key, holding the key and its VLog offset) and vlog_active.dat (the append-only value bytes). Backup captures both. The in-memory index is not backed up — it is rebuilt from the WAL on startup, so backing up the WAL and VLog is sufficient to reconstruct full key-value state.
Full backup
A full backup captures a consistent snapshot of the entire dataset as of the moment it runs, while the engine keeps serving traffic. It checkpoints the WAL, then copies each disk's VLog up to its snapshotted end offset (using pread64, safe alongside concurrent appends) plus the compacted WAL, and writes a manifest.json describing the result.
go build -o veltrixdb-backup ./cmd/backup
# Full backup to local directory
veltrixdb-backup full --data-dirs=/mnt/nvme0,/mnt/nvme1 --dest=/backup/2026-05-26
# Full backup, straight to S3
veltrixdb-backup full-cloud --data-dirs=/data \
--provider=s3 --bucket=my-bucket --region=us-east-1
Full and incremental backups are safe to run against a live engine; the VLog copy only reads bytes that were already durable at snapshot time, so appends that land after the snapshot are simply excluded from that backup.
Incremental backup
An incremental backup captures only the VLog bytes written since a base backup, plus a fresh copy of the (small) compacted WAL, which always reflects every live key at that moment:
veltrixdb-backup incremental --data-dirs=/data \
--dest=/backup/incr-20260523-150000 --base=/backup/full-20260523
Because VLog offsets are stable — values are never moved except by GC — each incremental delta is a self-contained, ordered byte range. A backup chain looks like:
full-T1 <- incr-T2 <- incr-T3 <- incr-T4
Restoring to T4 requires every backup in the chain; VLog deltas are concatenated in order (full_vlog + delta_T2 + delta_T3 + delta_T4) and the WAL is taken from the latest backup in the chain, since it already reflects all live keys at that point.
Cloud upload
The backup tool uploads to S3, GCS, or Azure with the same manifest format used locally:
# Upload an existing local backup
veltrixdb-backup upload --src=/backup/2026-05-26 \
--provider=s3 --bucket=my-bucket --region=us-east-1
# Download a cloud backup
veltrixdb-backup download --provider=s3 --bucket=my-bucket \
--cloud-path=veltrix/full-1748001600000000000 --dest=/tmp/restore
# List everything in cloud storage
veltrixdb-backup list-cloud --provider=s3 --bucket=my-bucket --region=us-east-1
Cloud credentials are read from the provider's standard environment variables rather than passed as flags: AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION / AWS_SESSION_TOKEN for S3, GOOGLE_APPLICATION_CREDENTIALS (or GCS_ACCESS_TOKEN) for GCS, and AZURE_STORAGE_ACCOUNT / AZURE_STORAGE_KEY / AZURE_STORAGE_CONTAINER for Azure.
Restore
# Restore from a local backup chain (oldest-first)
veltrixdb-backup restore \
--chain=/backups/full-20260523,/backups/incr-20260523-150000 \
--data-dirs=/mnt/nvme0,/mnt/nvme1
# Download from cloud, then restore
veltrixdb-backup download --bucket s3://my-bucket/veltrix/ \
--backup-id full-1748001600000000000 --out /backups/full
veltrixdb-backup restore --chain=/backups/full --data-dirs=/data-new
Restore copies the full backup's VLog into the destination, appends each incremental delta in order, and copies the WAL from the latest backup in the chain onto the destination. Once restore completes, start the engine normally — replayWAL() rebuilds the in-memory index from the restored WAL, and the engine begins serving traffic with a cold cache but correct data.
Point-in-time recovery (PITR)
For recovery finer than backup granularity, VeltrixDB supports continuous WAL archiving. A background WALArchiver copies newly durable WAL entries (with their value bytes resolved inline, so each archived segment is self-contained and safe from VLog GC) into an archive directory as they're fsynced. Enable archiving before taking the base full backup, since restore needs archive coverage from the backup point forward:
# Inspect what's in the archive
veltrixdb-backup archive-status --archive=/backup/wal-archive
# Restore to an exact wall-clock moment
veltrixdb-backup restore-pitr \
--base-backup=/backup/full-20260702 \
--archive=/backup/wal-archive \
--until=2026-07-02T14:30:00Z \
--data=/data-new
# Restore to an exact single write
veltrixdb-backup restore-pitr \
--base-backup=/backup/full-20260702 \
--archive=/backup/wal-archive \
--until=version:123456 \
--data=/data-new
restore-pitr requires the engine to be stopped and the target --data directories to be fresh and empty — it refuses to touch a directory that already contains engine files. Archive segments are verified with per-segment CRC32C before replay; a corrupted segment aborts the restore rather than silently applying a torn record.
Recommended backup strategy
A common pattern is a daily full backup plus hourly incrementals, which lets you restore to any hour by applying the full plus N incrementals. For finer recovery, run continuous WAL archiving alongside a daily full and use restore-pitr to land on an exact timestamp or write version.
Retention guidance: keep at least 7 daily full backups plus all incrementals for the latest full — older incrementals are useless once their base full is deleted. Size the archive's MaxArchiveAgeSec / MaxArchiveBytes so it always spans back to the oldest full backup you intend to restore from.
After every backup run, verify: manifest.json exists and its engine_version matches the live engine, vlog_end_off per disk has increased, and each disk's wal.log size is proportional to the number of live keys.
What is not backed up
| Item | Why excluded | Recovery |
|---|---|---|
Raft log (raft_state.gob) | Cluster state, not needed for single-node restore | Recreated on startup |
| In-memory LIRS cache | Volatile by design | Cache warms up after restart |
| Bloom filters | Rebuilt from index on startup | Automatic |
Segment files (seg_*.dat) | Superseded by WAL + VLog | Not needed |
| Prometheus metrics | Ephemeral | Not needed |