Litestream — Implementation
Scope: Litestream SQLite replication to Cloudflare R2 Tests: All passing (0 failures, 0 errors)
1. Gem Added
File: Gemfile
gem "litestream"
- litestream — Bundles the Litestream binary and integrates with Puma as a plugin. No system package installation needed.
2. Configuration Files
2a. config/litestream.yml (NEW — generated + customized)
Litestream's native YAML configuration. Defines all 4 production databases and their S3-compatible replica targets. Customized from the generator output to add endpoint and force-path-style for Cloudflare R2 compatibility:
dbs:
- path: storage/production.sqlite3
replicas:
- type: s3
bucket: $LITESTREAM_REPLICA_BUCKET
path: storage/production.sqlite3
endpoint: $LITESTREAM_REPLICA_ENDPOINT
force-path-style: true
access-key-id: $LITESTREAM_ACCESS_KEY_ID
secret-access-key: $LITESTREAM_SECRET_ACCESS_KEY
# ... same pattern for production_cache, production_queue, production_cable
Key additions over the generated defaults:
endpoint: $LITESTREAM_REPLICA_ENDPOINT— required for Cloudflare R2 (non-AWS S3-compatible)force-path-style: true— required for R2's path-style URLs
2b. config/initializers/litestream.rb (NEW — generated + customized)
Maps Rails encrypted credentials to the environment variables that config/litestream.yml references. Falls back to Cloudflare R2 credentials if dedicated Litestream credentials aren't set:
Rails.application.configure do
litestream_credentials = Rails.application.credentials.litestream
r2_credentials = Rails.application.credentials.cloudflare
config.litestream.replica_bucket = litestream_credentials&.dig(:replica_bucket)
config.litestream.replica_key_id = litestream_credentials&.dig(:replica_key_id) || r2_credentials&.dig(:r2_access_key_id)
config.litestream.replica_access_key = litestream_credentials&.dig(:replica_access_key) || r2_credentials&.dig(:r2_secret_access_key)
account_id = litestream_credentials&.dig(:r2_account_id) || r2_credentials&.dig(:r2_account_id)
config.litestream.replica_endpoint = "https://#{account_id}.r2.cloudflarestorage.com" if account_id.present?
config.litestream.username = litestream_credentials&.dig(:dashboard_username) || "admin"
config.litestream.password = litestream_credentials&.dig(:dashboard_password)
end
3. Modified Files
3a. Modified: config/puma.rb
Added Litestream Puma plugin (runs replication inside the Puma process):
plugin :litestream if ENV["LITESTREAM_IN_PUMA"]
3b. Modified: config/routes.rb
Mounted Litestream dashboard behind admin authentication:
authenticate :user, ->(user) { user.admin? } do
mount Litestream::Engine, at: "/litestream"
end
3c. Modified: config/deploy.yml
Added Litestream env var to Kamal clear env:
env:
clear:
LITESTREAM_IN_PUMA: true
4. Summary of Files
New Files (2)
| File | Purpose |
|---|---|
config/litestream.yml |
Litestream replication config (4 DBs → R2) |
config/initializers/litestream.rb |
Litestream credentials mapping & dashboard auth |
Modified Files (4)
| File | Changes |
|---|---|
Gemfile |
Added gem "litestream" |
config/puma.rb |
Added Litestream Puma plugin |
config/routes.rb |
Mounted Litestream dashboard at /litestream |
config/deploy.yml |
Added LITESTREAM_IN_PUMA: true env var |