Litestream — Plan
Complexity: Low Integration: Litestream SQLite replication to Cloudflare R2 for near real-time backups
Overview
Litestream continuously replicates SQLite databases to S3-compatible storage (Cloudflare R2). This provides near real-time backups of all 4 production databases (primary, cache, queue, cable) with point-in-time recovery. Uses the litestream-ruby gem which bundles the Litestream binary and integrates with Puma.
- Reference: https://litestream.io/
- Gem: https://github.com/fractaledmind/litestream-ruby
External Setup
- In Cloudflare Dashboard: R2 Object Storage → Create bucket:
your-app-litestream(dedicated bucket for DB replicas) - Reuse existing R2 API Token, or create a dedicated one with Read & Write permissions scoped to the litestream bucket
- Note the same Account ID used for R2 image storage
Code Changes
- Add to
Gemfile:gem "litestream" bundle install- Run generator:
bin/rails generate litestream:install- Creates
config/litestream.yml(database replication config) - Creates
config/initializers/litestream.rb(Rails credentials mapping)
- Creates
- Configure
config/initializers/litestream.rbto pull from Rails credentials (with fallback to Cloudflare R2 credentials) - Update
config/litestream.ymlto addendpointandforce-path-stylefor each replica (required for R2/non-AWS S3-compatible storage) - Add Puma plugin in
config/puma.rb:plugin :litestream if ENV["LITESTREAM_IN_PUMA"] - Mount dashboard in
config/routes.rb(behind admin auth):authenticate :user, ->(user) { user.admin? } do mount Litestream::Engine, at: "/litestream" end - Add
LITESTREAM_IN_PUMA: truetoconfig/deploy.ymlenv clear section - Add credentials via
bin/rails credentials:edit:litestream: replica_bucket: "your-app-litestream" replica_key_id: "your-r2-access-key" replica_access_key: "your-r2-secret-key" r2_account_id: "your-32char-hex-account-id" dashboard_username: "admin" dashboard_password: "a-secure-password"
Verification
- In production, Puma starts with Litestream replication (check logs for "litestream" messages)
- R2 bucket contains replica files for all 4 databases
- Visit
/litestreamas admin → dashboard shows replication status - Test restore:
bin/rails litestream:restore -- --database storage/production.sqlite3 - Run
bin/ci— all existing tests still pass