Skip to content
Navigation

Type at least two characters. Search covers page titles, headings, tags and database names.

↑ ↓ to navigateEnter to openEsc to close0 pages
SQLite and RocksDBintermediate

Production Considerations

Running embedded databases in production — backups, upgrades, observability and the failure modes that have no operator to notice them.

3 min readIntermediateUpdated Edit this page

An embedded database has no separate process to monitor, no DBA and no operator watching it. Its health is your application's responsibility, and its failures surface as application bugs.

Storage is the whole game

Also confirm:

  • The filesystem honours fsync. Some virtualised and network-backed storage acknowledges writes before they are durable, which converts a clean crash into corruption.
  • There is free space for maintenance: VACUUM needs roughly the database size; RocksDB compaction needs headroom above the dataset.
  • File permissions match the process user, and the database is not world-readable if it holds anything sensitive.

Backups

There is no backup daemon, so the application or its deployment must take them.

  • SQLite — use the backup API or .backup, never a plain file copy. See File Management.
  • RocksDB — use its checkpoint or backup engine APIs, which produce a consistent snapshot; copying the directory while the process is running does not.

Copy the result off the host. An embedded database on a single machine has the same failure domain as that machine.

Upgrades

Both formats are stable, but the interaction with your application is not:

  • Migrations must run before the application uses the new schema, and both are in the same process — so a failed migration is a failed startup. Make migrations idempotent and safe to retry.
  • A downgrade may not be possible. If a new version writes a format an older build cannot read, rolling back the application means restoring a backup.
  • Test the upgrade against a copy of a real database, including one that has been in use long enough to accumulate the shapes that only appear in production.

Observability

Nothing reports on an embedded database unless the application does. At minimum, emit:

  • Database file size and its growth rate.
  • Query duration percentiles from the application's own instrumentation.
  • Lock contention: SQLITE_BUSY counts, or RocksDB write stall time.
  • Integrity check results from a scheduled job.
  • Backup success and age.
# A periodic health check worth running.
row = conn.execute("PRAGMA quick_check").fetchone()
if row[0] != "ok":
    metrics.increment("sqlite.integrity_failure")

Concurrency assumptions

  • SQLite — one writer at a time. Serialise writes through a single connection or a queue in the application, set busy_timeout, and use BEGIN IMMEDIATE for read-then-write transactions. See Concurrency and WAL.
  • RocksDB — concurrent writers are supported, but write stalls are the mechanism by which it pushes back. Handle them as backpressure rather than as an error.

When to stop using an embedded database