Skip to content

Instantly share code, notes, and snippets.

@researcx
Last active May 31, 2023 19:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save researcx/aff274bf5f806af0cf77d23da4c8aa34 to your computer and use it in GitHub Desktop.
Save researcx/aff274bf5f806af0cf77d23da4c8aa34 to your computer and use it in GitHub Desktop.
proxmox containers backup
#!/bin/bash -l
# set -x
# make sure you have zip installed
# running: /path/to/proxmox-backup.sh [0/1]
# use 0 for debugging, 1 to run
# gist.github.com/researcx
# configuration
backups_folder="/Backups/Pi/$hostname" # needs to exist
path="/tmp/backup-$hostname/"
path_host=$path"host/" # main sysetm (host) files
path_containers="/var/lib/vz/dump" # proxmox container backup storage
mail=1 # email a log and important server information (disk space, etc)
mailto="root"
make_encrypted=0 # luks encrypt the host files and lxd container images
encryption_passphrase="passphrase" # passphrase for luks encrypted container
path_crypt="luks/"
crypt_ext=".crypt"
days=7 # delete backups older than x days
run=$1 # whether to actually run commands (set to 0 for debugging)
wait=120 # amount of seconds to wait between running backup commands, helps calm server load
timestamp=$(date +%Y%m%d_%H%M%S)
# host files (this is kind of a catch-all) (good to get via history | grep "nano /etc"), space separated
host_files=("/root/.bashrc" "/root/.ssh" "/root/.bash_profile" "/root/.bash_history" "/root/.tmux.conf" "/root/.local/share/fish" "/root/Scripts" "/etc/wireguard" "/etc/logrotate.d" "/etc/profile" "/etc/netdata" "/etc/fish" "/etc/fail2ban" "/etc/ssh" "/etc/sysctl.conf" "/etc/cron.d" "/etc/cron.daily" "/etc/cron.weekly" "/etc/cron.hourly" "/etc/cron.deny" "/etc/crontab" "/var/spool/cron" "/etc/sysconfig" "/etc/fstab" "/etc/crypttab" "/etc/postfix" "/etc/hosts" "/etc/resolv.conf" "/etc/aliases" "/etc/rsyslog.d" "/etc/ufw" "/etc/pam.d" "/etc/netplan" "/etc/wpa_supplicant" "/etc/network" "/etc/networks" "/etc/apt" "/etc/apticron" "/etc/yum.repos.d" "/etc/iptables.rules" "/etc/ip6tables.rules" "/etc/iptables" "/etc/modprobe.d" "/etc/pve" "/etc/udev" "/etc/modules-load.d" "/etc/systemd" "/etc/update-motd.d" "/etc/lightdm" "/etc/groups" "/etc/passwd" "/etc/nsswitch.conf" "/etc/netatalk" "/etc/samba" "/etc/avahi" "/etc/default" "/etc/nanorc" "/etc/X11" "/etc/netconfig")
# proxmox containers, numbers, space separated
core=("100" "101" "102" "103") # dnscrypt, nginx, ldap, ircd
# log
log_file=backup-$hostname-$timestamp.log
log=$path$log_file
# make the directories
rm -r $path
rm -r $path_containers
mkdir -p $path
mkdir -p $path_host
mkdir -p $path_containers
mkdir -p $backups_folder
if [[ "$make_encrypted" == 0 ]]; then
mkdir -p $path$path_crypt
fi
# functions
convertsecs() {
((h=${1}/3600))
((m=(${1}%3600)/60))
((s=${1}%60))
printf "%02d:%02d:%02d\n" $h $m $s
}
proxmox_backup() {
container=$1
vzdump $container
pct unlock $container
}
make_encrypted_container(){
name=$1
file=$2
mountpoint="$2/enc/"
size=$(du -s $file | awk '{print $1}')
if [ "$size" -lt "65536" ]; then # cryptsetup: luks images need to be +32mb in order to be able to be formatted/opened
size=65536
else
size="$(($size + 65536))" #just being safe! hopefully
fi
crypt_filename=$hostname-$name-$timestamp$crypt_ext
crypt_mapper=$hostname-$name-$timestamp
crypt_devmapper="/dev/mapper/$crypt_mapper"
fallocate -l "$size"KB $path$path_crypt$crypt_filename
printf $encryption_passphrase | cryptsetup luksFormat $path$path_crypt$crypt_filename -
printf $encryption_passphrase | cryptsetup luksOpen $path$path_crypt$crypt_filename $crypt_mapper
mkfs -t ext4 $crypt_devmapper
mkdir -p $mountpoint
mount $crypt_devmapper $mountpoint
}
unmount_encrypted_container(){
name=$1
mountpoint="$2/enc/"
crypt_mapper=$hostname-$name-$timestamp
crypt_devmapper="/dev/mapper/$crypt_mapper"
umount $mountpoint
cryptsetup luksClose $crypt_mapper
}
# clean up old backups
if [[ "$run" == 1 ]]; then
if [[ "$make_encrypted" == 1 ]]; then
find $backups_folder -maxdepth 1 -name "*$crypt_ext" -type f -mtime +$days -print -delete >> $log
fi
find $backups_folder -maxdepth 1 -name "*.log" -type f -mtime +$days -print -delete >> $log
find $backups_folder -maxdepth 1 -name "*.tar" -type f -mtime +$days -print -delete >> $log
find $backups_folder -maxdepth 1 -name "*.zip" -type f -mtime +$days -print -delete >> $log
fi
# start main code
START_TIME=$(date +%s)
echo "Backup:: Script start -- $timestamp" >> $log
echo "Backup:: Host: $hostname -- Date: $timestamp" >> $log
echo "Paths:: Host: $path" >> $log
echo "Paths:: Containers: $path_containers" >> $log
echo "Paths:: Backups: $backups_folder" >> $log
# host files
echo "Backup:: Backing up the following host files to $path_host" >> $log
# echo $host_files >> $log
for host_file in ${host_files[@]}; do
echo "Backup:: Starting backup of $host_file to $path_host" >> $log
host_file_safe=$(echo $host_file | sed 's|/|-|g')
if [[ "$run" == 1 ]]; then
zip -r $path_host$host_file_safe-$timestamp.zip "$host_file" >> $log
fi
done
echo "Backup:: Host files successfully backed up" >> $log
if [[ "$run" == 1 ]]; then
if [[ "$make_encrypted" == 1 ]]; then
echo "Backup:: Making an encrypted container for host files" >> $log
make_encrypted_container "host" $path_host
echo "Backup:: Moving files to encrypted container" >> $log
mv $path_host/*.zip "$path_host/enc/"
echo "Backup:: Unmounting encrypted container" >> $log
unmount_encrypted_container "host" $path_host
rm -rf $path_host/*
echo "Backup:: Successfully encrypted host backup" >> $log
fi
fi
# containers
echo "Backup:: Backing up containers" >> $log
for container in ${core[@]}; do
echo "Backup:: Starting backup on $container to $path_containers" >> $log
if [[ "$run" == 1 ]]; then
proxmox_backup $container >> $log
sleep $wait
fi
done
if [[ "$run" == 1 ]]; then
if [[ "$make_encrypted" == 1 ]]; then
echo "Backup:: Making an encrypted container for containers" >> $log
make_encrypted_container "containers" $path_containers
echo "Backup:: Moving files to encrypted container" >> $log
mv $path_containers/*.tar.gz "$path_containers/enc/"
echo "Backup:: Unmounting encrypted container" >> $log
unmount_encrypted_container "core" $path_containers
rm -rf $path_containers/*
echo "Backup:: Successfully encrypted core container backup" >> $log
fi
sleep $wait
fi
rsync -a --progress $log $backups_folder >> $log
if [[ "$make_encrypted" == 1 ]]; then
rsync -a --progress $path$path_crypt $backups_folder >> $log
else
rsync -a --progress $path_host $backups_folder >> $log
rsync -a --progress $path_containers/ $backups_folder >> $log
fi
END_TIME=$(date +%s)
# end main code
elapsed_time=$(( $END_TIME - $START_TIME ))
echo "Backup :: Script End -- $(date +%Y%m%d_%H%M)" >> $log
echo "Elapsed Time :: $(convertsecs $elapsed_time) " >> $log
backup_size=`find $path -maxdepth 5 -type f -mmin -360 -exec du -ch {} + | grep total$ | awk '{print $1}'`
backup_stored=`find $path -maxdepth 5 -type f -exec du -ch {} + | grep total$ | awk '{print $1}'`
disk_remaining=`df -Ph $backups_folder | tail -1 | awk '{print $4}'`
echo -e "Subject: [$hostname] Backup Finished [$backup_size] [stored: $backup_stored | disk remaining: $disk_remaining] (took $(convertsecs $elapsed_time))\n\n$(cat $log)" > $log
if [[ "$mail" == 1 ]]; then
sendmail -v $mailto < $log
fi
sleep $wait
rm -r $path
rm -r $path_containers
@docop
Copy link

docop commented May 31, 2023

Hi i look at your script and not sure how it work... Was it it encrypt vm and then zip the thing, put this into folder : "/var/lib/vz/dump and send a mail ? Thanks for precision. As i don't figure on how to do proper backup.. hehe

@researcx
Copy link
Author

Hi i look at your script and not sure how it work... Was it it encrypt vm and then zip the thing, put this into folder : "/var/lib/vz/dump and send a mail ? Thanks for precision. As i don't figure on how to do proper backup.. hehe

Hi!

It runs vzdump on each VM listed in core on line 34
https://gist.github.com/researcx/aff274bf5f806af0cf77d23da4c8aa34#file-proxmox-backup-sh-L34

path_containers (/var/lib/vz/dump by default) is the location where the vzdump images/backups get saved by vzdump, if this location is different for you, then specify it, otherwise leave it

if make_encrypted is set to 1 then it will make LUKS images to put the folder containing the vzdump images and host files such as things from /etc/ into

the idea of this script is to encrypt VM and host backups if you want to upload them somewhere

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment