This article documents several practises when deploying SQLite as a production database for a Rails app.


1) Separate databases

In SQLite, each database file has its own locking and WAL state. Separating main, queue, cache, and cable into different files helps isolate spikes in each file.

development:
  primary:
    <<: *default
    database: storage/main.sqlite3
  queue:
    <<: *default
    database: storage/queue.sqlite3
    migrations_paths: db/queue_migrate
  cache:
    <<: *default
    database: storage/cache.sqlite3
    migrations_paths: db/cache_migrate
  cable:
    <<: *default
    database: storage/cable.sqlite3
    migrations_paths: db/cable_migrate

2) Tune configuration via pragmas

  • cache_size - The SQLite page cache size, which has a default of 2MB. More in-memory cache reduces disk operations.
  • busy_timeout - Default is 0, which results in a BUSY error when the database is locked. Setting a timeout allows it to retry.
  • temp_store - Use memory instead of disk for temporary files.

Some of these tuning trade off memory usage for better performance. Please consider carefully if you are constrained by memory size.

default: &default
  adapter: sqlite3
  pool: 5
  timeout: 5000
  pragmas:
    journal_mode: wal
    synchronous: normal
    cache_size: -8192
    mmap_size: 0
    temp_store: memory
    busy_timeout: 5000
    foreign_keys: on

3) Backups

Please also set up a backup plan and test it by restoring it.