Skip to content

Backup & Restore

Engram’s state lives in three places. All three need backing up.

  1. Postgres — notes, metadata, users, DEKs (encrypted), Oban jobs
  2. MinIO (or your S3 bucket) — attachments
  3. ENCRYPTION_MASTER_KEY — stored outside the cluster, see Encryption Setup

Qdrant can be rebuilt from scratch by reindexing — it’s a derivative of Postgres data. Backing it up is optional and only saves the time of re-embedding.

Use pg_dump:

Terminal window
docker compose exec -T postgres \
pg_dump -U engram engram --clean --if-exists \
| gzip > engram-db-$(date +%F).sql.gz

For larger deployments, set up streaming WAL backups with a tool like pgBackRest or Barman. Both have first-class tutorials for Postgres in Docker; pick whichever your ops setup already uses.

mc mirror is the simplest path:

Terminal window
mc mirror --overwrite local/engram-attachments remote/engram-backup

Where local is your MinIO alias and remote is wherever you ship backups.

Order matters:

  1. Restore Postgres first. The DEKs in the DB are still wrapped by ENCRYPTION_MASTER_KEY.
  2. Restore the master key. Without it, Postgres data is bytea garbage.
  3. Restore the attachment bucket. Notes reference attachments by key — if attachments are missing, notes still load but their embeddings/attachments 404.
  4. Boot the app. It runs migrations if needed, then the boot canary verifies the master key matches the DB.
  5. (Optional) Reindex Qdrant. If Qdrant wasn’t backed up, you can rebuild it by re-enqueuing every note’s embedding job — the Oban worker pipeline will repopulate Qdrant from Postgres. A first-class mix engram.reindex task is on the roadmap; until then, an IEx loop over Engram.Notes.list_all/0 + the EmbedNote worker does the job. Reindexing only costs you the re-embedding time, not data integrity.
  • Postgres dump is older than 24h? Bad. Cron it.
  • Master key is in 2+ separate physical locations? Required.
  • Attachment bucket has versioning enabled? Strongly recommended.
  • Restore drill done at least once? Do it before you need it.
  • MCP OAuth tokens issued to clients are invalidated by a restore (they reference state in DB). Clients need to re-connect.
  • WebSocket sync state — Obsidian plugins reconnect automatically and reconcile against the restored DB.
  • Anything written after the last backup. Plan your RPO accordingly.