Skip to content

Backups and restore

Everything a Beluga store is lives in two places: the database and the uploaded images. On a Droplet or a Fly volume that is a disk you own, and no one is backing it up unless you are.

  • The database. data/beluga.sqlite for SQLite, or the Postgres database.
  • The images. ASSETS_DIR (public/assets by default), unless they are in a bucket, in which case the provider’s versioning or replication is your backup.
  • The environment. .env or the platform’s secrets. The Stripe webhook secret and the session secret are not recoverable from anywhere else, and a restored store with a new session secret signs everyone out, which is fine, while a restored store with the wrong webhook secret records no orders, which is not.

Not Stripe. Products, Prices, Customers and payments are Stripe’s, and survive anything that happens to your server.

Copy the file while the database is consistent. The safe way is SQLite’s own backup command, which works on a live database:

Terminal window
sqlite3 data/beluga.sqlite ".backup 'backups/beluga-$(date +%F).sqlite'"

A plain cp of a file that is mid-write can produce a corrupt copy. Sessions and the webhook queue are in the database too, so a restore rewinds them; that is harmless, since delivery is at-least-once and a session just expires.

Terminal window
rsync -a public/assets/ backups/assets/

Derivatives are regenerated only on upload, so back up the whole tree, not just the originals.

pg_dump on a schedule, or the managed provider’s point-in-time recovery. The Fly and DigitalOcean managed offerings both do daily snapshots by default; check the retention window is longer than the time it would take you to notice a problem.

On a VM, a cron entry that runs the SQLite backup and the rsync, then copies the result off the box with rclone or aws s3 sync, is the minimum. Test the restore once: point a fresh checkout at the copied database and directory, boot it, and look at an order.

  1. Stop the process.
  2. Put the database file and the assets directory back where DATABASE_URL and ASSETS_DIR point.
  3. Start it. Migrations are idempotent, so a backup from an older release is brought up to date at boot.
  4. Check that the Stripe webhook secret in the environment is the one the live endpoint has, and take a test payment.