Checklist

SQLite Disaster Recovery Checklist

Borela field notes / 6 min read

A practical checklist for small SaaS apps, internal tools, and self-hosted apps running SQLite in production.

The checklist is for a bad day

Disaster recovery sounds oversized for a small SQLite app until the database is the only copy of customer data. Then the checklist is not bureaucracy. It is the difference between restoring calmly and discovering that the backup plan was mostly vibes.

This checklist is intentionally practical. It is written for a small production app on a VPS, a Rails SQLite deployment, an internal tool, or a self-hosted app where one person is probably both developer and operator.

1. Define backup freshness

Know the timestamp of the latest backup acknowledgement. Decide the maximum data loss your app can tolerate, then alert before that threshold is crossed. If you would be upset losing an hour of data, do not set the alert at twenty-four hours.

Freshness should be visible without SSH. A dashboard, health endpoint, or email alert is enough. The important part is that stale backup state reaches you before a restore is needed.

2. Keep backups off the app server

A local copy is convenient, but it does not protect you from disk loss, accidental deletion, provider trouble, or a bad deploy that removes the wrong directory. Put the replica in object storage or another machine with separate credentials.

Use credentials scoped as narrowly as practical. The app-side agent should not need broad cloud permissions just to write backup data.

3. Prove the restore path

Restore into a clean temporary location. Run integrity_check. Open the database with the app or a read-only sqlite3 session. Count the tables that matter. Write down the result.

This is the step most teams skip because the backup tool seems trustworthy. The tool may be trustworthy and the restore can still fail because the path, token, bucket, endpoint, timestamp, or retention policy changed.

borela-agent restore -project app -output /tmp/restored.db
sqlite3 /tmp/restored.db 'PRAGMA integrity_check;'

4. Decide retention for human mistakes

Keep enough history to recover from mistakes discovered later. A seven-day window is a good starting point for small apps. It covers the weekend, a bad deploy noticed Monday, and the common case where users report data weirdness after the fact.

Retention is a product decision as much as an infrastructure decision. More history costs more storage, but too little history turns a recoverable bug into a permanent data loss incident.

5. Write the incident note now

Write the exact restore command before you need it. Include where to find the API key, how to list recovery points, where to restore the database, how to point the app at the restored file, and who should be told before switching traffic.

The middle of an outage is the wrong time to remember whether the bucket name used dashes, underscores, or the project slug from two years ago.

borela-agent snapshots -project app
borela-agent restore -project app -timestamp 2026-04-25T18:00:00Z -output /tmp/restored.db

6. Practice the boring version

Do one restore while the app is healthy. Time it. Screenshot or save the result. If it takes twenty minutes, that is useful to know. If it fails, that is even more useful to know.

After the first manual restore, automate as much as you can. The goal is not to become a backup hobbyist. The goal is to make the recovery path quiet, routine, and hard to forget.

A backup you have never restored is still a guess.

Let Borela run the restore drill every week.