kamal-backup

kamal-backup

The easiest way to run scheduled backups for a Rails app deployed with Kamal. [![Gem Version](https://img.shields.io/gem/v/kamal-backup.svg)](https://rubygems.org/gems/kamal-backup) [![CI](https://github.com/crmne/kamal-backup/actions/workflows/ci.yml/badge.svg)](https://github.com/crmne/kamal-backup/actions/workflows/ci.yml) [![Docker Image](https://img.shields.io/badge/image-ghcr.io%2Fcrmne%2Fkamal--backup-blue)](https://github.com/crmne/kamal-backup/pkgs/container/kamal-backup) [![Docs](https://img.shields.io/badge/docs-kamal--backup.dev-blue)](https://kamal-backup.dev) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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.

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
  • a fast way to restore production data locally
  • restore drills that do not touch the live production database
  • evidence that says more than "the backup job is green"

kamal-backup packages that workflow into a small Ruby gem, a production accessory image, and 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:
        - PGPASSWORD
        - RESTIC_PASSWORD
        - AWS_ACCESS_KEY_ID
        - AWS_SECRET_ACCESS_KEY
    volumes:
      - "chatwithwork_storage:/data/storage:ro"
      - "chatwithwork_backup_state:/var/lib/kamal-backup"

Put the backup settings in config/kamal-backup.yml:

accessory: backup
app_name: chatwithwork
database_adapter: postgres
database_url: postgres://chatwithwork@chatwithwork-db:5432/chatwithwork_production
backup_paths:
  - /data/storage
restic_repository: s3:https://s3.example.com/chatwithwork-backups
restic_init_if_missing: true
backup_schedule_seconds: 86400

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 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 evidence

What you get

  • Scheduled backups: the accessory runs continuously and backs up on backup_schedule_seconds.
  • 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: pull production backups into your local Rails app when you need to inspect real data.
  • Restore drills: restore into scratch production-side targets and record the result.
  • Security review evidence: kamal-backup evidence prints redacted JSON with latest snapshots, checks, drills, retention, and tool versions.

Docs

Read the full documentation at kamal-backup.dev.

Start here:

License

MIT