Back Up and Replicate a Django SQLite Database With Litestream
Use Litestream to continuously replicate a Django SQLite database to S3-compatible storage: enable WAL mode, run Litestream as a sidecar or systemd service, restore on startup in containers, test point-in-time recovery, and know the limits of single-writer SQLite.
Problem statement
SQLite is a reasonable production database for many small Django apps: fast, no separate server, one file. The weakness is durability. If the server or volume is lost, so is everything since the last backup, and copying the file while the app writes can produce a corrupt backup. Litestream solves this by continuously streaming SQLite's write-ahead log to object storage, giving you backups seconds behind live data and point-in-time restore.
- Enable WAL mode for the Django SQLite database (Django 5.1+ supports an
init_commandoption; otherwise runPRAGMA journal_mode=WALonce). - Install Litestream and configure the database path and an S3-compatible replica in
/etc/litestream.yml. - Run Litestream alongside Django: as a systemd service, or with
litestream replicate -exec "gunicorn ..."in a container. - In containers with ephemeral disks, run
litestream restore -if-replica-existsbefore starting the app. - Test restores regularly, and keep exactly one writer process on one server.
Step-by-step solution
1. Configure SQLite for production
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": "/data/db.sqlite3",
"OPTIONS": {
"transaction_mode": "IMMEDIATE",
"timeout": 5,
"init_command": "PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL; PRAGMA busy_timeout=5000;",
},
}
}
transaction_mode and init_command options were added in Django 5.1. WAL mode lets readers proceed while one writer writes, and Litestream requires it. Keep the database in a directory that the app user can write to.
2. Configure Litestream
# /etc/litestream.yml
dbs:
- path: /data/db.sqlite3
replicas:
- type: s3
bucket: shop-sqlite-backups
path: prod/db
endpoint: https://<account>.r2.cloudflarestorage.com
region: auto
retention: 168h
sync-interval: 1s
Credentials come from LITESTREAM_ACCESS_KEY_ID and LITESTREAM_SECRET_ACCESS_KEY environment variables, or an IAM role on AWS. Litestream periodically writes snapshots and continuously uploads WAL segments; retention controls how far back you can restore.
3. Run Litestream with the app
On a VM, install the package and enable its service:
sudo systemctl enable --now litestream
In a container, let Litestream supervise Gunicorn so both start and stop together:
#!/bin/sh
set -e
litestream restore -if-db-not-exists -if-replica-exists /data/db.sqlite3
python manage.py migrate --noinput
exec litestream replicate -exec "gunicorn config.wsgi --bind 0.0.0.0:8000 --workers 2"
If the container's disk is ephemeral (Fly.io Machines without volumes, Cloud Run), the restore step recreates the database from the replica on every start.
4. Restore and test
litestream restore -o /tmp/restore.sqlite3 /data/db.sqlite3
litestream restore -o /tmp/restore.sqlite3 -timestamp 2026-10-03T08:00:00Z /data/db.sqlite3
sqlite3 /tmp/restore.sqlite3 "PRAGMA integrity_check;"
Schedule a restore test and compare row counts with production. A backup that has never been restored is not proven.
5. Respect SQLite's single-writer model
Litestream does not make SQLite multi-server. Exactly one instance must write to the database file, on one machine. Run one container or VM, not several replicas; scaling out needs PostgreSQL or a different replication approach. Running two Litestream processes against the same replica path corrupts the backup history.
Explanation
In WAL mode, SQLite appends changes to a -wal file and periodically checkpoints them into the main database. Litestream holds a read transaction to control checkpointing, copies new WAL frames to object storage, and writes snapshots of the full database at intervals. Restoring means downloading the latest snapshot before the target time and replaying WAL segments up to it. Because it reads the WAL rather than copying a live file, backups are consistent without stopping the app.
Edge cases / notes
- Media files. Litestream only covers the SQLite file. Store uploads in object storage or back them up separately.
- Checkpoints. Avoid running
VACUUMor manual checkpoints from other tools while Litestream runs; let Litestream manage checkpointing. - Version changes. Litestream has evolved its replica format across major versions; check upgrade notes before upgrading production.
Next steps
- Decide whether SQLite fits with Run Django on SQLite in Production.
- Handle concurrency errors with Fix SQLite Database Is Locked in Django.
- Cover the rest with the Django Backup and Recovery Checklist.
FAQ
Does Litestream let me run several Django servers on SQLite?
No. It provides backups and restore, not multi-writer replication. Keep one writer.
Do I need WAL mode?
Yes. Litestream works by reading SQLite's write-ahead log.
How much data can I lose with Litestream?
Typically only the last second or so of writes, depending on the sync interval.
Where can Litestream store backups?
S3 and S3-compatible services such as Cloudflare R2, Backblaze B2, and MinIO, plus other supported backends.