Skip to main content
Stackbastion

How to back up your Lovable or Supabase app (and actually test the restore)

7 July 2026· 3 min read · by Stackbastion

Lovable Cloud now includes daily backups. They’re kept for 14 days. That’s a real improvement over having nothing. But a backup you’ve never restored is a rumor, not a backup. And 14 days doesn’t cover everything. A bug can corrupt data quietly for three weeks before anyone notices. Here’s how to set up a backup you can actually trust, on your own Postgres instance.

Why daily snapshots aren’t the same as real backup

A daily snapshot only protects you against things you notice within 14 days. It also usually restores to last midnight, not to the exact moment before something went wrong. Point-in-time recovery, or PITR, fixes both problems. You keep a longer history. And you can restore to any second, not just to last night.

Step 1: set up continuous backup with PITR

If you run your own Postgres, pgBackRest is the standard tool for this. Here’s the pattern: a full backup on a schedule. Smaller backups in between. And a constant stream of write-ahead logs, so you can replay to any moment.

# One-time: initialize the backup repository
pgbackrest --stanza=main --repo1-path=/var/backups/pgbackrest stanza-create

# Weekly: full backup
pgbackrest --stanza=main --type=full backup

# Nightly: incremental backup
pgbackrest --stanza=main --type=incr backup

Add this line to postgresql.conf: archive_command = 'pgbackrest --stanza=main archive-push %p'. Now every log gets saved as it’s written, not just at backup time.

Step 2: get a copy off the server

A backup on the same machine as your database protects you against nothing. If that machine dies, the backup dies with it. Point pgBackRest’s storage at somewhere else: an S3-compatible bucket, or a provider like Hetzner Storage Box. Encrypt it while you’re there.

# repo1-cipher-type=aes-256-cbc in pgbackrest.conf, with a real passphrase
# repo1-path pointed at your off-server storage, not local disk

Step 3: the part everyone skips — actually test a restore

This step turns “I think we have backups” into “I know we have backups.” Restore to a separate, isolated copy. Never restore over your live database:

# Restore to a fresh, isolated data directory — never over the live one
pgbackrest --stanza=main --type=time \
  "--target=2026-07-16 14:30:00" \
  --pg1-path=/var/lib/postgresql/restore-test \
  restore

Start Postgres against that restored copy. Run a query for a row you know should be there. Time the whole thing. That number is your real recovery time. Not a guess.

Step 4: set up dead-man’s-switch alerting

A backup that quietly stopped running three weeks ago is worse than no backup at all, because you think you’re covered. Have each successful backup ping a heartbeat monitor — a simple cron job hitting a health-check URL works fine. Send an alert if a heartbeat is missed for more than 24 hours.

Or, we do it for you

Setting this up correctly, restore drill included, is part of every Stackbastion plan — see what’s included. It’s also the core of our Full Production Rescue + Migration, if you’d rather have someone else do the migration.

FAQ

Do I need PITR if I already have daily snapshots?

Daily snapshots are better than nothing. But they only cover 14 days, and they only restore to midnight. PITR gives you a longer window and a more exact restore point.

How often should I actually run the restore test?

Do it once when you first set things up. Do it again any time you change your schema. After that, once a month is a fair pace for a small app.

What’s a reasonable recovery time for a small app?

There’s no single right answer. It depends what your users expect during an outage. Time your own restore first. Then decide if that number is good enough.