kamal-backup
Add scheduled Rails backups to Kamal with one accessory. [](https://rubygems.org/gems/kamal-backup) [](https://github.com/crmne/kamal-backup/actions/workflows/ci.yml) [](https://github.com/crmne/kamal-backup/pkgs/container/kamal-backup) [](https://kamal-backup.dev) [](LICENSE)Backups for Rails apps deployed with Kamal should not become a separate ops project.
kamal-backup is one Kamal accessory that runs encrypted backups for your Rails database and file-backed Active Storage files on a schedule. It also gives you restore drills and redacted evidence for security reviews like CASA.
Run kamal-backup init, fill in one config file, add the accessory, then boot it. If you already deploy with Kamal, backups should feel like adding one more accessory.
Why Rails teams use it
Most self-hosted Rails apps need the same things:
- scheduled backups for PostgreSQL, MySQL/MariaDB, or SQLite
- file-backed Active Storage backups from mounted volumes
- local restores for inspecting production data safely
- restore drills that do not touch the live production database
- evidence that says more than "the backup ran"
kamal-backup wraps that workflow in a small Ruby gem and a production accessory image backed by a restic repository.
Quick start
Add the gem:
group :development do
gem "kamal-backup"
end
Generate the config file and accessory snippet:
bundle install
bundle exec kamal-backup init
Add the accessory to config/deploy.yml:
accessories:
backup:
image: ghcr.io/crmne/kamal-backup:latest
host: chatwithwork.com
files:
- config/kamal-backup.yml:/app/config/kamal-backup.yml:ro
env:
secret:
- DATABASE_PASSWORD
- RESTIC_PASSWORD
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
volumes:
- "chatwithwork_storage:/data/storage:ro"
- "chatwithwork_backup_state:/var/lib/kamal-backup"
For SQLite databases stored on the mounted storage volume, omit :ro from that volume.
Put the backup settings in config/kamal-backup.yml:
app: chatwithwork
accessory: backup
databases:
- name: app
adapter: postgres
url: postgres://chatwithwork@chatwithwork-db:5432/chatwithwork_production
password:
secret: DATABASE_PASSWORD
paths:
- /data/storage
restic:
repository: s3:https://s3.example.com/chatwithwork-backups
password:
secret: RESTIC_PASSWORD
init_if_missing: true
backup:
schedule: 1d
Boot it. The container runs kamal-backup schedule by default:
bundle exec kamal-backup validate
bin/kamal accessory boot backup
bin/kamal accessory logs backup
Run the first backup, check the repository, and print evidence. From an app checkout with config/deploy.yml, these commands shell out through Kamal to the backup accessory:
bundle exec kamal-backup backup
bundle exec kamal-backup list
bundle exec kamal-backup check
bundle exec kamal-backup evidence
What you get
- Scheduled backups: the accessory runs continuously and backs up on
backup.schedule. - Database and Active Storage coverage: database dumps plus file-backed Active Storage files from mounted volumes.
- Restic underneath: encrypted, deduplicated snapshots in S3-compatible storage, a restic REST server, or a filesystem repository.
- Local restores: inspect production data safely in your local Rails app.
- Restore drills: restore into scratch production-side targets, run verification commands, and record the result.
- Security review evidence:
kamal-backup evidenceprints redacted JSON with latest snapshots,kamal-backup checkresults, drills, retention, and tool versions.
Docs
Read the full documentation at kamal-backup.dev.
Start here:
Releasing
Run the release helper from a clean master checkout:
bin/release 0.2.9
It updates lib/kamal_backup/version.rb, runs the test suite and docs build, commits Release 0.2.9, and pushes master. CI publishes the RubyGem and Docker image tags first, then creates v0.2.9, the GitHub release, and the docs deployment from the release commit.
Use bin/release 0.2.9 --no-push to prepare the commit locally without publishing.
License
MIT