Simple Capistrano Backup
I wanted to have a dead simple script to create a backup of the files and the database on the deployer home folder with a capistrano task. It could use some refactor, and it could be made more flexible in many aspects, but it works and it’s just a small bunch of lines.
There are good backup libraries that you let you encrypt, split, upload to multiple places and be notified whether has been a problem or not. They also take care of automating the restoring process, and let you switch between different versions and remove old backups. Setting that up takes more time though, this script is only valid for simple situations, it doesn’t compare to those libraries but it sure beats not having any script at all.
I added explicitely the PGPASSWORD
to the dump command, but there are better ways, you can add a .pgpass
file to your home folder, and add a --no-password
flag (more info here). You can also add it as an environment variable
Another improvement is to make it run periodically using a cron job: cron-jobs-and-rails
# lib/capistrano/tasks/simple_backup.rake
set :database_staging, "database_staging"
set :database_production, "database_production"
# IMPORTANT -> put a path to the folder, not the symlink, like "../shared/public/uploads"
set :files_to_backup_relative_path, "../shared/public/uploads"
namespace :deploy do
desc "Dump the DB and tar the uploads to the deployer home"
task :simple_backup do
stage = fetch(:stage)
database = fetch("database_#{stage}".to_sym)
raise "Missing database name" unless database
scp_commands = []
time_stamp = Time.now.strftime("%F-%H-%M-%S")
database_dump_name = "dump_#{database}_#{time_stamp}"
database_dump_cmd = "PGPASSWORD=********* pg_dump #{database} -U postgres -h localhost > #{database_dump_name}"
perform_files_backup = !!fetch(:files_to_backup_relative_path)
if perform_files_backup
files_path = File.expand_path(File.join(release_path, fetch(:files_to_backup_relative_path)))
files_parent_path = File.expand_path("../", files_path)
files_path_basedir = File.basename(files_path)
files_backup_name = "files_#{stage}_#{time_stamp}.tar.gz"
end
puts "\n ### STARTING BACKUP ON #{stage.upcase} AT #{time_stamp}"
on roles(:app) do |server|
puts "\n ### DUMPING THE DATABASE"
scp_base_cmd = "scp #{server.user}@#{server.hostname}:~/"
execute database_dump_cmd
scp_commands << scp_base_cmd + database_dump_name + " ."
if perform_files_backup
files_compression_cmds = []
files_compression_cmds << "cd #{files_parent_path}"
files_compression_cmds << "tar zczf #{files_backup_name} #{files_path_basedir}"
files_compression_cmds << "mv #{files_backup_name} ~"
files_compression_cmd = files_compression_cmds.join(" && ")
puts "\n ### COMPRESSING FILES AT #{files_path}"
puts files_compression_cmd
execute files_compression_cmd
scp_commands << scp_base_cmd + files_backup_name + " ."
end
puts "\n GET THE FILES WITH:\n"
scp_commands.each{|cmd| puts cmd}
puts "\n\n"
end
end
end
After executing it, you get the lines needed to download the files
bundle exec cap staging deploy:simple_backup
scp deployer@[IP]:~/files_production_2015-06-16-15-33-28.tar.gz .
scp deployer@[IP]:~/dump_database_production_2015-06-16-15-33-28 .