The model
Litestream runs as a daemon next to the SQLite app. It puts the database into WAL mode (if not already), then continuously reads new WAL frames and uploads them to a configured replica. Periodically it also takes full snapshots of the database file. Restore = "download the latest snapshot, then replay the WAL segments after it." Replication is one-way and append-only; the destination is not a live SQLite endpoint.
Install
# Debian / Ubuntu — download the .deb from GitHub
LS_VER=0.3.13
curl -L -o /tmp/litestream.deb \
"https://github.com/benbjohnson/litestream/releases/download/v${LS_VER}/litestream-v${LS_VER}-linux-amd64.deb"
sudo apt install /tmp/litestream.deb
# Or single-binary install
curl -L "https://github.com/benbjohnson/litestream/releases/download/v${LS_VER}/litestream-v${LS_VER}-linux-amd64.tar.gz" \
| sudo tar -xz -C /usr/local/bin litestream
The Debian package installs the litestream binary, a sample /etc/litestream.yml, and a systemd unit at litestream.service (disabled by default).
Configure a replica
Edit /etc/litestream.yml:
access-key-id: "<aws-access-key>"
secret-access-key: "<aws-secret-key>"
dbs:
- path: /var/lib/app/app.db
replicas:
- type: s3
bucket: my-app-litestream
path: prod/app.db
region: us-east-2
# Snapshot the full db every 24h; retain 7 days of snapshots+WAL.
snapshot-interval: 24h
retention: 168h
retention-check-interval: 1h
# Sync to replica every 1s (default).
sync-interval: 1s
For non-AWS S3-compatible (B2, R2, Wasabi, MinIO), set the endpoint: alongside bucket: and region: us-east-1 (most providers accept any region string).
For multiple databases, list each under dbs:. For multiple replica targets per database (e.g. S3 + a second S3 in another region), list each under that db's replicas:. Litestream uploads to all of them concurrently.
Run as a service
sudo systemctl enable --now litestream
sudo journalctl -u litestream -f
Watch for "writing snapshot" and "sync written" log lines. Within a minute the bucket has a snapshot and a stream of small WAL segments.
Restore on a new machine
Install Litestream there too, copy /etc/litestream.yml (or a minimal one with credentials + bucket + path) over, then:
sudo systemctl stop my-app
sudo litestream restore -o /var/lib/app/app.db \
s3://my-app-litestream/prod/app.db
sudo systemctl start my-app
litestream restore picks the latest snapshot and replays WAL segments on top of it, producing a file byte-identical to (or extremely close to) what the source database looked like at the last sync. With the default 1s sync interval, you lose at most ~1 second of writes from a crash that destroyed the source disk.
Point-in-time restore:
sudo litestream restore -o /tmp/app-rewind.db \
-timestamp 2026-02-27T15:00:00Z \
s3://my-app-litestream/prod/app.db
What goes wrong without it
SQLite is single-machine durable; what it's not, on its own, is offsite-durable. The disk dies, the machine dies, or someone runs rm at 3am, and the database goes with it. A cron'd sqlite3 .backup gives you nightly snapshots, but loses up to a day of writes. Litestream is the missing piece: continuous, off-machine, append-only, encrypted-at-rest (by the bucket), tiny daemon, ~zero application changes.
Application-side: WAL mode is required
Litestream needs the database to be in WAL mode. Most modern SQLite ORMs do this automatically; if not, run once at startup:
PRAGMA journal_mode = WAL;
PRAGMA busy_timeout = 5000;
WAL is also faster for concurrent reads with one writer — the same use case where SQLite makes sense in the first place.
Litestream is read-only against the database file via the SQLite API — it does not touch the WAL pages directly. Do not use it on a database that is being concurrently rsynced/copied, or that uses non-WAL journal modes. The "one writer, multiple readers via SQLite" assumption is what makes the whole approach safe.
HA: read-only replica via livestream
The litestream replicate/litestream restore pair can also continuously hydrate a remote read-only copy of the database. On the standby:
sudo litestream replicate \
-config /etc/litestream-pull.yml \
-no-expand-env \
-exec "sleep infinity"
With a config that uses type: s3 as the source. The local app.db stays roughly in sync; a failover script promotes it by stopping Litestream and pointing the app at the local file. Round-trip RPO is a few seconds; failover is whatever your DNS or load-balancer health check takes.
Limits worth knowing
- Single writer. Litestream is for a SQLite app that only ever runs from one process. Putting a load balancer in front of two instances both writing to the same Litestream-backed database is not a supported model — for that, use Litestream's successor project, LiteFS.
- Storage costs are tiny but real. A small daily snapshot + WAL adds up to maybe 100–500 MB/month for an average app database; S3 standard storage at $0.023/GB is cents.
- The replica is not queryable. It's a WAL stream and snapshot archive in S3, not a hot replica. Use
litestream restoreto materialize a queryable file.
Within those limits — one-writer SQLite app, durable offsite replica, sub-second RPO — Litestream is, depending on the metric, the cheapest or the simplest durability story available for SQLite. Often both.