litestream-coder
This skill guides configuring Litestream for continuous SQLite backup in Rails 8+ apps. Use when setting up production backups for SQLite databases (Solid Queue, Solid Cache, Solid Cable).
Installation
Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.
Voir la source ↗GuidesInstructions
Litestream Coder
Overview
Litestream provides continuous streaming backup for SQLite databases to S3-compatible storage. Essential for Rails 8+ apps using SQLite in production with Solid Queue, Solid Cache, and Solid Cable.
Configuration File
Create config/litestream.yml:
dbs:
- path: /rails/storage/production.sqlite3
replicas:
- type: s3
bucket: ${BUCKET_NAME}
path: sqlite/production
endpoint: ${S3_ENDPOINT}
region: ${S3_REGION}
access-key-id: ${LITESTREAM_ACCESS_KEY_ID}
secret-access-key: ${LITESTREAM_SECRET_ACCESS_KEY}
force-path-style: true # Required for S3-compatible storage
sync-interval: 5s # How often to sync WAL
snapshot-interval: 1h # Full snapshot frequency
retention: 168h # 7 days
retention-check-interval: 1h
validation-interval: 12h
Retention Strategies by Database Type
Primary database (production.sqlite3):
-
sync-interval: 5s- Frequent syncing for data durability -
snapshot-interval: 1h- Hourly snapshots -
retention: 720h- 30 days for recovery
Cache database (production_cache.sqlite3):
-
sync-interval: 10s- Less critical, reduce load -
snapshot-interval: 6h- Less frequent -
retention: 24h- 1 day sufficient
Queue database (production_queue.sqlite3):
-
sync-interval: 5s- Important for job durability -
snapshot-interval: 1h- Hourly -
retention: 72h- 3 days for debugging
Cable database (production_cable.sqlite3):
-
sync-interval: 10s- Ephemeral data -
snapshot-interval: 6h- Infrequent -
retention: 24h- 1 day
Complete Configuration Example
dbs:
- path: /rails/storage/production.sqlite3
replicas:
- type: s3
bucket: myapp-backups
path: sqlite/production
endpoint: https://fsn1.your-objectstorage.com
region: fsn1
access-key-id: ${LITESTREAM_ACCESS_KEY_ID}
secret-access-key: ${LITESTREAM_SECRET_ACCESS_KEY}
force-path-style: true
sync-interval: 5s
snapshot-interval: 1h
retention: 720h
retention-check-interval: 1h
validation-interval: 12h
- path: /rails/storage/production_cache.sqlite3
replicas:
- type: s3
bucket: myapp-backups
path: sqlite/cache
endpoint: https://fsn1.your-objectstorage.com
region: fsn1
access-key-id: ${LITESTREAM_ACCESS_KEY_ID}
secret-access-key: ${LITESTREAM_SECRET_ACCESS_KEY}
force-path-style: true
sync-interval: 10s
snapshot-interval: 6h
retention: 24h
- path: /rails/storage/production_queue.sqlite3
replicas:
- type: s3
bucket: myapp-backups
path: sqlite/queue
endpoint: https://fsn1.your-objectstorage.com
region: fsn1
access-key-id: ${LITESTREAM_ACCESS_KEY_ID}
secret-access-key: ${LITESTREAM_SECRET_ACCESS_KEY}
force-path-style: true
sync-interval: 5s
snapshot-interval: 1h
retention: 72h
- path: /rails/storage/production_cable.sqlite3
replicas:
- type: s3
bucket: myapp-backups
path: sqlite/cable
endpoint: https://fsn1.your-objectstorage.com
region: fsn1
access-key-id: ${LITESTREAM_ACCESS_KEY_ID}
secret-access-key: ${LITESTREAM_SECRET_ACCESS_KEY}
force-path-style: true
sync-interval: 10s
snapshot-interval: 6h
retention: 24h
Kamal 2 Deployment
Add Litestream as an accessory in config/deploy.yml:
accessories:
litestream:
image: litestream/litestream:0.3
host: <SERVER_IP>
cmd: replicate
volumes:
- "myapp_storage:/rails/storage:ro" # Read-only access
files:
- config/litestream.yml:/etc/litestream.yml
env:
secret:
- LITESTREAM_ACCESS_KEY_ID
- LITESTREAM_SECRET_ACCESS_KEY
options:
health-cmd: "litestream databases || exit 1"
health-interval: 30s
health-timeout: 5s
health-retries: 3
Key points:
- Mount storage as read-only (
:ro) - Litestream only reads WAL files - Use same volume name as main app
- Health check verifies Litestream can see databases
Secrets Configuration
In .kamal/secrets:
LITESTREAM_ACCESS_KEY_ID=$(op read "op://myproject/production-s3/access_key_id")
LITESTREAM_SECRET_ACCESS_KEY=$(op read "op://myproject/production-s3/secret_access_key")
Disaster Recovery
Restore Database
# Stop application first
kamal app stop
# Restore from latest backup
docker run --rm \
-v myapp_storage:/rails/storage \
-e LITESTREAM_ACCESS_KEY_ID="$(op read 'op://myproject/production-s3/access_key_id')" \
-e LITESTREAM_SECRET_ACCESS_KEY="$(op read 'op://myproject/production-s3/secret_access_key')" \
litestream/litestream:0.3 restore \
-config /etc/litestream.yml \
/rails/storage/production.sqlite3
# Restart application
kamal app start
Point-in-Time Recovery
docker run --rm \
-v myapp_storage:/rails/storage \
-e LITESTREAM_ACCESS_KEY_ID="..." \
-e LITESTREAM_SECRET_ACCESS_KEY="..." \
litestream/litestream:0.3 restore \
-config /etc/litestream.yml \
-timestamp "2025-01-15T10:30:00Z" \
/rails/storage/production.sqlite3
Monitoring
Check backup status:
# Via Kamal
kamal accessory exec litestream -- litestream databases
# Direct on server
docker exec myapp-litestream litestream databases
S3-Compatible Storage
Litestream works with any S3-compatible storage:
| Provider | Endpoint Example |
|---|---|
| Hetzner Object Storage | https://fsn1.your-objectstorage.com |
| Backblaze B2 | https://s3.us-west-002.backblazeb2.com |
| DigitalOcean Spaces | https://nyc3.digitaloceanspaces.com |
| AWS S3 | (omit endpoint, use region) |
Critical: Always set force-path-style: true for non-AWS S3-compatible storage.
Best Practices
- Separate databases by retention needs - Main DB needs longer retention than cache
-
Monitor backup lag -
litestream databasesshows replication status - Test restores regularly - Don't wait for a disaster to verify backups work
-
Use read-only mount - Litestream only reads WAL files,
:roprevents accidents
Exemples
Consultez les exemples du dépôt source.
Voir la source ↗Historique source
- Date d’indexation
- 2026-01-20
- Mise à jour de l’index
- 2026-02-04
- Branch
- main
Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!