Skip to content
dihlabs.lol

Operations

Restoring a backup

Backups are in /var/backups/optz/feed-<UTC stamp>.db.gz, each with a .sha256 of the uncompressed file. Every backup already passed PRAGMA integrity_check before it was kept.

On this page

1. Pick and verify the backup

cd /var/backups/optz
ls -1t feed-*.db.gz | head                      # newest first
B=feed-YYYYMMDDTHHMMSSZ.db.gz                   # the one to restore
gunzip -c "$B" > /tmp/restore.db
sha256sum /tmp/restore.db; cat "$B.sha256"      # the two hashes must match
python3 -c "import sqlite3; d=sqlite3.connect('/tmp/restore.db'); print(d.execute('PRAGMA integrity_check').fetchone()[0], d.execute('SELECT count(*), max(id) FROM posts').fetchone())"

Expect ok and a plausible post count and max id.

2. Swap it in

sudo systemctl stop optz                         # clean stop: drains and checkpoints the WAL
sudo mv /var/lib/optz/feed.db /var/lib/optz/feed.db.bad-$(date +%s)
sudo rm -f /var/lib/optz/feed.db-wal /var/lib/optz/feed.db-shm   # stale WAL/shm from the old file
sudo install -o optz -g optz -m 0640 /tmp/restore.db /var/lib/optz/feed.db
sudo -u optz python3 -c "import sqlite3; sqlite3.connect('/var/lib/optz/feed.db').execute('PRAGMA journal_mode=WAL')"
sudo systemctl start optz

Never leave a -wal file from a different database next to the restored one. SQLite would try to apply it.

3. Check

curl -s http://127.0.0.1:$(sed -n 's/^PORT=//p' /etc/optz/env)/health    # {"status":"ok",...}
curl -s http://127.0.0.1:$(sed -n 's/^PORT=//p' /etc/optz/env)/feed | head -c 300
journalctl -u optz -n 20 --no-pager

Writes made after the backup's timestamp are lost unless an off-box replica (below) has them. Keep feed.db.bad-* until you're sure it isn't needed.

Off-box copies

The backup unit has a commented-out ExecStartPost for rclone copy to object storage. That's the recommended off-box path: copies that have already been verified, on a fixed schedule. Litestream can stream the WAL continuously instead (point-in-time restore). Its last release I could confirm was v0.5.0 (Oct 2025), so check that it's still maintained before relying on it.