We all know how important database backups are. Even if functionalities like Multi AZ support, multiple replicas, it's importance can't be more emphasised.

Ruby has a handy wrapper to automate this.

Setup Requirements:

  1. A linux server (proper disk space should be there, atleast the size of your database)
  2. Ruby installation. (rvm/rbenv can be used)
  3. Slack/Flock/Mattermost/SMTP (*not required, but good to have)
  4. Place where to store the backup. I personally prefer S3. It can be local, FTP or anything of that sort.

Process:

  1. Install backup gem. As I write this post, 4.3.0 is the latest release out there. You can install the same with gem install backup

    However, there are certain problems on some systems with the installation. If you face any error, try installing the nextrc candidate gem install backup -v5.0.0.beta.1
  2. Backup gem provides a cli utility for ease. Generate the config file by running this task: backup generate:model --trigger producion_backup --archives --storages='s3' --databases='postgresql' --compressor='gzip' . The flags are self explanatory IMO.
  3. Open up the generated file:
    vim ~/Backup/models/production_backup.rb
  4. Replace, and tweak the needed items:
# encoding: utf-8

Model.new(:producion_backup, 'Description for producion_backup') do
  archive :configs do |archive|
    archive.add "/home/ubuntu/projects/api/shared/config/"
    archive.add '/etc/nginx/'
  end

  database PostgreSQL do |db|
    db.name               = "db_name"
    db.username           = "db_username"
    db.password           = "db_password"
    db.host               = "db_url.ap-south-1.rds.amazonaws.com" # or localhost, or anyother hosted service url
    db.port               = 5432 # or any other port that you might have
  end

  # Keep 4 monthly backups
  #      6 weekly
  #      7 daily
  # and  23 daily backups
  time = Time.now
  if time.hour == 0
    if time.day == 1  # first day of the month
      storage_id = :monthly
      keep = 4
    elsif time.sunday?
      storage_id = :weekly
      keep = 7
    else
      storage_id = :daily
      keep = 6
    end
  else
    storage_id = :hourly
    keep = 23
  end

  store_with S3 do |s3|
    # AWS Credentials
    s3.access_key_id     = "AWS_access_key_id"
    s3.secret_access_key = "AWS_secret_access_key"
    s3.region            = "ap-south-1"
    s3.bucket            = "db-backups"
    s3.path              = "/backups/#{storage_id}"
    s3.keep              = keep
  end

  # can use mattermost/Flock or anyother servicee that takes in webhooks.
  notify_by Slack do |slack|
    slack.on_success           = true
    slack.on_failure           = true
    slack.webhook_url          = 'Webhook URL'
    slack.username             = 'Backup bot'
    slack.channel              = 'db-backup-alerts'
    slack.icon_emoji           = ':ice:'
  end

  compress_with Gzip
end

5. Create a bucket in S3 before running the trigger.

6. Trigger the backup with backup perform --trigger production_backup

7. Verify the backup after it's done and uploaded over to S3.

8. Put up a cron job for automating this, 0 * * * * /bin/bash -l -c 'rvm use 2.6.3 && backup perform --triggert producion_backup'. The job will run every hour.

9. Why the server hard disk needs to be large? Because this gem creates an archive of the tables and other items in local first, and then uploads it.

I hope the article was helpful for you!

Documentation and more tweaking options for Backup gem can be found here.