Simple SQLite Backups for Rails 8 with SSHKit#
This won’t be a long post, I just wanted to record something I struggled with for a little while. I recently made a quick toy app using Rails 8 as a learning experience. I don’t actually need database backups since this is only a toy app, but I thought it sounded like a fun project that wouldn’t take too long.
It ended up taking longer than I thought because I had to play around with the user and group permissions on my VM a little bit, and directly accessing a named Docker volume from the host side turned out to be more difficult than I thought.
Eventually, I realized that I could just use docker volume inspect $VOLUME_NAME
and then parse out the mount point property.
I tried to mess with the folder permissions so my admin user could access the docker volumes without using sudo
, but I eventually gave up and started using sudo
, and then everything worked perfectly.
Sometimes, worse really is better.
This project was also a fun excuse to play around with #:href “”SSHKit, which is a Ruby library for running commands on remote servers. My use case was really simple since I only have one host and I don’t have any complex logic. My favorite feature is the ability to #:href “”transfer files. Before settling on using SSHKit, I was looking into various other methods for automating this process using bash scripting, and none of them looked fun. But SSHKit makes transferring a file back to the host side super simple. This was my first experience replacing some of my normal bash scripting with Ruby, but it was a really positive one.
#!/usr/bin/env ruby
require "sshkit"
require "sshkit/dsl"
require "date"
require "json"
include SSHKit::DSL
SSHKit::Backend::Netssh.configure do |ssh|
ssh.ssh_options = {
user: 'admin',
keys: ['.ssh/kamal'],
forward_agent: false,
auth_methods: ['publickey'],
}
end
environment = "production"
on 'emoji-polls.australorp.dev' do
file_name = "#{DateTime.now.strftime("%Y-%m-%d")}-#{environment}.db"
temp_file_path = "/tmp/#{file_name}"
local_image_path = "./images/#{file_name}"
info "Backing up image to #{temp_file_path} on #{host}"
volumes_output = capture :docker, "volume", "inspect", "emoji_polls_storage"
volumes_info = JSON.parse volumes_output
volume_path = volumes_info[0]["Mountpoint"]
execute :sudo, :sqlite3, "#{volume_path}/#{environment}.sqlite3", "'.backup #{temp_file_path}'"
download! temp_file_path, local_image_path
info "Deleting image on #{host}"
execute :sudo, :rm, temp_file_path
end