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.

Don't bypass SQLite's own locking

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 restore to 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.