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

  1. In Cloudflare Dashboard: R2 Object Storage → Create bucket: your-app-litestream (dedicated bucket for DB replicas)
  2. Reuse existing R2 API Token, or create a dedicated one with Read & Write permissions scoped to the litestream bucket
  3. Note the same Account ID used for R2 image storage

Code Changes

  1. Add to Gemfile: gem "litestream"
  2. bundle install
  3. Run generator: bin/rails generate litestream:install
    • Creates config/litestream.yml (database replication config)
    • Creates config/initializers/litestream.rb (Rails credentials mapping)
  4. Configure config/initializers/litestream.rb to pull from Rails credentials (with fallback to Cloudflare R2 credentials)
  5. Update config/litestream.yml to add endpoint and force-path-style for each replica (required for R2/non-AWS S3-compatible storage)
  6. Add Puma plugin in config/puma.rb:
    plugin :litestream if ENV["LITESTREAM_IN_PUMA"]
    
  7. Mount dashboard in config/routes.rb (behind admin auth):
    authenticate :user, ->(user) { user.admin? } do
      mount Litestream::Engine, at: "/litestream"
    end
    
  8. Add LITESTREAM_IN_PUMA: true to config/deploy.yml env clear section
  9. 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 /litestream as admin → dashboard shows replication status
  • Test restore: bin/rails litestream:restore -- --database storage/production.sqlite3
  • Run bin/ci — all existing tests still pass