How do I back up a SQLite database on a live server?
Run sqlite3 storage/production.sqlite3 ".backup /tmp/out.sqlite3" inside the container, then copy the result off the host. The .backup command takes a consistent snapshot while writes continue; a plain cp of a live database gives you a file that looks fine and fails on restore. Store it somewhere the server cannot reach.
The command that is safe to run while the app is serving
bin/kamal app exec 'sqlite3 storage/production.sqlite3 ".backup /tmp/backup.sqlite3"'
ssh your-server 'docker cp $(docker ps -qf name=web):/tmp/backup.sqlite3 ~/backup.sqlite3'
scp your-server:~/backup.sqlite3 ./backup-$(date +%F).sqlite3
Three steps because there are three boundaries: inside the container, on the host, and off the machine. The third one is the one people skip, and it is the one that matters.
Why cp is not good enough
SQLite writes in pages, and a write-ahead log holds changes that are not yet in the main file. A
cp catches whichever pages happened to be flushed at that instant, plus a -wal file you probably
did not copy. The result opens without complaint and is missing recent writes, or is corrupt in a
way you discover during a restore at the worst possible moment.
.backup uses SQLite's own backup API, which takes a consistent snapshot across both files.
Verify the backup, because an unverified backup is a guess
sqlite3 backup-$(date +%F).sqlite3 'PRAGMA integrity_check;'
sqlite3 backup-$(date +%F).sqlite3 'SELECT COUNT(*) FROM accounts;'
integrity_check should print ok. A row count you recognize tells you it is the right database and
not an empty one, which is the other failure people discover late.
Make it happen without you
A backup you run by hand happens until the week you are busy. Put it in a job and schedule it with
Solid Queue: a class under app/jobs, listed in config/recurring.yml. The same container runs it,
so there is nothing new to deploy. Upload the result to object storage with the S3 credentials that
config/storage.yml already reads.
What this does not cover
Uploaded files. They live on the same volume but are not in the database, so a database backup
misses them entirely. If you are storing attachments on local disk rather than S3, back up storage
as well.