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

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.

databasesqliteproduction