Backups & restore

Back up and restore the data stores behind a self-hosted instance

A self-hosted instance keeps state in a few stores: Postgres for application data, ClickHouse for the observability records (spans and traces), and MinIO for object storage. Those three are the ones to back up. Redis is a cache (sign-ins, locks, rate limits, pub/sub), so it rebuilds on its own. RabbitMQ holds the task queue: losing it drops in-flight background jobs, so drain it before planned downtime rather than backing it up.

If you’ve already moved to managed data stores, your provider’s backup tooling replaces everything below.

The named volumes

The compose project name is futureagi, so every volume is prefixed futureagi_:

VolumeHoldsBack up?
futureagi_postgres-dataPostgres application dataYes
futureagi_clickhouse-dataClickHouse spans and tracesYes
futureagi_minio-dataMinIO objectsYes
futureagi_fi-collector-dataSpans the collector couldn’t write to ClickHouseDon’t delete it
futureagi_rabbitmq-dataRabbitMQ task queueNo, drain instead
futureagi_redis-dataRedis cacheNo, rebuildable
futureagi_peerdb-catalog-dataPeerDB replication catalogNo, rebuilt by re-running init
futureagi_peerdb-minio-dataPeerDB staging objectsNo, transient

Note

futureagi_fi-collector-data is a dead-letter queue, not a cache. It holds spans that failed to reach ClickHouse, usually while ClickHouse was briefly down, and they’re replayed once it’s back. Deleting the volume drops that telemetry for good, so don’t prune it with the rebuildable ones.

Postgres

Postgres holds the application data, so back it up on a schedule. Use the custom format, and pass -T so docker compose exec doesn’t allocate a TTY and mangle the binary dump. Stop the backend and workers before restoring, since --clean drops objects the running app may be holding open:

# Backup
docker compose exec -T postgres \
  pg_dump -U futureagi -d futureagi --format=custom \
  > backup-$(date +%F).dump

# Restore
docker compose exec -T postgres \
  pg_restore -U futureagi -d futureagi --clean --if-exists \
  < backup-2026-04-22.dump

ClickHouse

ClickHouse holds data that exists nowhere else. The fi-collector writes spans straight into it and Django dual-writes traces, so neither is recoverable from Postgres. Back it up on its own schedule:

docker compose exec -T clickhouse clickhouse-client --query \
  "BACKUP DATABASE default TO S3('s3://your-bucket/ch-backup/', 'KEY', 'SECRET')"

Restore the same way, with the path of the backup you took:

docker compose exec -T clickhouse clickhouse-client --query \
  "RESTORE DATABASE default FROM S3('s3://your-bucket/ch-backup/', 'KEY', 'SECRET')"

Warning

Don’t rely on PeerDB init to rebuild ClickHouse. It restores the mirrored Postgres tables, not the spans the collector writes directly. ClickHouse needs a real backup on its own schedule.

MinIO

Mirror the MinIO bucket to S3 with the MinIO client:

mc alias set local http://localhost:9005 futureagi <MINIO_ROOT_PASSWORD>
mc alias set s3 https://s3.amazonaws.com <AWS_KEY> <AWS_SECRET>

mc mirror local/ s3/your-bucket/     # back up
mc mirror s3/your-bucket/ local/     # restore

Dive deeper

Was this page helpful?

Questions & Discussion