database · sqlite · production
SQLite in Prod: WAL, Volumes, and Nightly Backups
How a single-file database survives production: WAL mode, persistent volumes, and a 10-line cron backup.

Boring Team
September 6, 2026 · 1 min read
Most teams outgrow SQLite before they outgrow their first 10,000 users. The failure is rarely the engine — it is the setup: DELETE journal mode, ephemeral disk, zero backups.
Boring's SQLite runs in WAL mode from the first boot (journal_mode = WAL, foreign_keys = ON). Writers append, readers never block, and foreign keys actually cascade. That alone removes the two classic SQLite production incidents.
The second rule is mechanical: the database must live on a persistent volume. Locally that is ./db/app.db; in Docker it is SQLITE_PATH=/db/app.db on a mounted volume. Ephemeral disk means the database vanishes with the container — the most expensive one-line misconfiguration in self-hosting.
The third rule is a 10-line script. npm run backup:db copies the main file plus WAL sidecars (-wal, -shm) — all three, or the backup is corrupt — keeps the last 24, and is meant for hourly cron. Analytics lives in a separate analytics.sqlite so telemetry writes never contend with product writes.
When traffic finally demands Postgres or Atlas, flip DB_PROVIDER. Same verbs, same routes. Until then, the boring file database is a production database.