├── README.md └── backup.sh /README.md: -------------------------------------------------------------------------------- 1 | # Simple backup script for GNU/Linux servers 2 | #### Simple bash backup script V1.2 3 | 4 | You need megatools in order to upload your backup file to MEGA. Download megatools from http://megatools.megous.com/ 5 | 6 | ### Main features 7 | 8 | - Backup custom files and directories 9 | - Backup MySQL/PostgreSQL/MongoDB databases 10 | - Copy/SCP/FTP to another server or mounted media 11 | - Backup GitLab 12 | - Upload to MEGA.nz cloud 13 | - Send a notification to your email 14 | - Logging all the activities 15 | - Encrypts backup file using GPG 16 | - Backup multiple MariaDB/MySQL docker containers 17 | 18 | ### Edit the configuration and run 19 | ``` 20 | sudo bash backup.sh 21 | ``` 22 | 23 | ### Syntax for Backup Docker Mariadb/Mysql 24 | ``` 25 | containerID:::user:::password:::database 26 | ``` 27 | ### Minio backup requirment on servers 28 | 29 | ``` 30 | mc config host add 31 | 32 | ``` 33 | for example 34 | 35 | ``` 36 | mc config host add minio http://192.168.1.51 BKIKJAA5BMMU2RHO6IBB V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 --api S3v4 37 | ``` 38 | 39 | and replace minio with minio_cluster_name 40 | 41 | ### Requirements 42 | 43 | - tar 44 | - gzip 45 | - bzip2 46 | 47 | ### Changelog 48 | 49 | **V1.2** 50 | 51 | - Added GitLab backup with gitlab-rake 52 | 53 | **V1.3** 54 | - Added Minio backup 55 | 56 | ### Main Requirement 57 | 58 | - tar 59 | - gzip 60 | - bzip2 61 | - mc 62 | 63 | ### TODO 64 | 65 | - [ ] Docker volume backup support 66 | - [ ] rsync directory files ( backup public or huge files) 67 | -------------------------------------------------------------------------------- /backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # bash-backup V1.2 4 | ################################################################# 5 | # You need megatools in order to upload your backup file to MEGA 6 | # Download megatools from http://megatools.megous.com/ 7 | ################################################################# 8 | # Simple backup script for GNU/Linux servers 9 | # Main features: 10 | # - Backup custom files and directories 11 | # - Backup MySQL/PostgreSQL/MongoDB databases 12 | # - Copy/SCP/FTP to another server or mounted media 13 | # - Backup GitLab 14 | # - Upload to MEGA.nz cloud 15 | # - Send a notification to your email 16 | # - Logging all the activities 17 | # - Encrypts backup file using GPG 18 | # - Backup multiple MariaDB/MySQL docker containers 19 | # 20 | # Edit the configuration and run: 21 | # $ sudo bash backup.sh 22 | # 23 | # Please help to simplify and develop new features 24 | # Narbeh - http://narbeh.org - narbeh.aj@gmail.com 25 | ################################################################# 26 | 27 | ################ 28 | # Configuration 29 | ################ 30 | 31 | # Server Name 32 | server_name="hostname" 33 | 34 | # Backup path 35 | backup_path="/tmp" 36 | 37 | # Script log file 38 | log_file="/var/log/backup.log" 39 | 40 | # Files to backup (Multi value) 41 | backup_files_enable="no" 42 | backup_files="/root/.bash_history /etc/passwd" 43 | 44 | # Directories to backup (Multi value) 45 | backup_dir_enable="no" 46 | backup_directories="/etc /var/log /usr/local" 47 | 48 | # backup sync directory to MinIO (Multi value) 49 | backup_to_minio_enable="no" 50 | minio_directories="/etc /var/log /usr/local" 51 | minio_bucket="" 52 | minio_cluster_name="" 53 | 54 | # Copy to other media (Multi value) 55 | external_copy="no" 56 | external_storage="/mnt" 57 | 58 | # Copy tar backup to MinIo 59 | external_minio_copy="no" 60 | external_minio_bucket="" 61 | external_minio_cluster_name="" 62 | 63 | # SCP to other server (Trusted servers for now) 64 | scp_enable="no" 65 | scp_server="1.2.3.4" 66 | scp_port="22" 67 | scp_username="root" 68 | scp_path="/media/backups" 69 | 70 | # Enable iptables backup 71 | iptables_backup="no" 72 | 73 | # Upload to FTP server (Using curl command) 74 | ftp_enable="no" 75 | ftp_server="1.2.3.4" 76 | ftp_path="/backups" 77 | ftp_username="" 78 | ftp_password="" 79 | 80 | # Send an email the result of the backup process 81 | # You should have sendmail or postfix installed 82 | send_email="no" 83 | email_to="test@gmail.com" 84 | 85 | # Encrypt archive file using GPG 86 | gpg_enable="no" 87 | gpg_public_recipient="" 88 | 89 | # Upload to MEGA.nz if you have installed the client. 90 | # /Root/ is the main directory in MEGA.nz 91 | mega_enable="no" 92 | mega_email="" 93 | mega_pass="" 94 | mega_path="/Root/backups" # /Root/ should always be here. 95 | 96 | # Full MySQL dump (All Databases) 97 | mysql_backup="no" 98 | mysql_user="" 99 | mysql_pass="" 100 | 101 | # Full PostgreSQL dump (All Databases) 102 | postgres_backup="no" 103 | postgres_user="" 104 | postgres_pass="" 105 | postgres_database="" 106 | postgres_host="localhost" 107 | postgres_port="5432" 108 | 109 | # MongoDB collection dump (MongoDB Version +3) 110 | mongo_backup="no" 111 | mongo_host="localhost" 112 | mongo_port="27017" 113 | mongo_database="" 114 | mongo_collection="" 115 | 116 | # Docker Mariadb/Mysql dump config 117 | # pattern of backup most be like containerID:::user:::password:::database 118 | # This script can backup multiple container with this pattern 119 | 120 | docker_mysql_backup="no" 121 | docker_mysql_containers="" 122 | 123 | ################################################################# 124 | ################################################################# 125 | ################################################################# 126 | 127 | ################ 128 | # Do the backup 129 | ################ 130 | 131 | case $1 in 132 | "--fresh" ) 133 | rm /var/backup_lock 2> /dev/null;; 134 | *) 135 | :;; 136 | esac 137 | 138 | # Main variables 139 | color='\033[0;36m' 140 | color_fail='\033[0;31m' 141 | nc='\033[0m' 142 | hostname=$(hostname -s) 143 | date_now=$(date +"%Y-%m-%d %H:%M:%S") 144 | 145 | # Checking lock file 146 | test -r /var/backup_lock 147 | if [ $? -eq 0 ];then 148 | echo -e "\n ${color}--- $date_now There is another backup process. \n${nc}" 149 | echo "$date_now There is another backup process." >> $log_file 150 | echo -e "\n ${color}--- $date_now If not, run the script with --fresh argument. \n${nc}" 151 | exit 152 | fi 153 | 154 | touch /var/backup_lock 2> /dev/null 155 | path_date=$(hostname -s)_$(date +"%Y-%m-%d-%H-%M-%S") 156 | mkdir -p $backup_path/Backup/$path_date 2>> $log_file 157 | echo -e "\n ${color}--- $date_now Backup started. \n${nc}" 158 | echo "$date_now Backup started." >> $log_file 159 | 160 | sleep 1 161 | 162 | # Backing up the files 163 | if [ $backup_files_enable = "yes" ] 164 | then 165 | echo -e "\n ${color}--- $date_now Backing up files \n${nc}" 166 | echo "$date_now Backing up files" >> $log_file 167 | mkdir $backup_path/Backup/$path_date/custom_files | tee -a $log_file 168 | for backup_custom_files in $backup_files 169 | do 170 | echo "--> $backup_custom_files" | tee -a $log_file 171 | cp $backup_files $backup_path/Backup/$path_date/custom_files/ 2>> $log_file 172 | done 173 | echo 174 | fi 175 | 176 | if [ $iptables_backup = "yes" ] 177 | then 178 | echo -e "\n ${color}--- $date_now Backing up iptables rules \n${nc}" 179 | echo "$date_now Backing up iptables rules" >> $log_file 180 | [ -d $backup_path/Backup/$path_date/custom_files ] || mkdir $backup_path/Backup/$path_date/custom_files 181 | iptables-save > $backup_path/Backup/$path_date/custom_files/iptables-save 182 | echo 183 | fi 184 | 185 | 186 | sleep 1 187 | 188 | # Backing up the directories 189 | if [ $backup_dir_enable = "yes" ] 190 | then 191 | echo -e "\n ${color}--- $date_now Backing up directories \n${nc}" 192 | echo "$date_now Backing up directories" >> $log_file 193 | for backup_dirs in $backup_directories 194 | do 195 | echo "--> $backup_dirs" | tee -a $log_file 196 | dir_name=`echo $backup_dirs | cut -d / -f2- | sed 's/\//-/g'` 197 | if [[ -d ${backup_dirs}/.git ]]; then 198 | tar -cjf $backup_path/Backup/$path_date/$dir_name.tar.bz2 -X ${backup_dirs}/.gitignore $backup_dirs/ > /dev/null 2> /dev/null 199 | else 200 | tar -cjf $backup_path/Backup/$path_date/$dir_name.tar.bz2 $backup_dirs/ > /dev/null 2> /dev/null 201 | fi 202 | done 203 | echo 204 | fi 205 | 206 | sleep 1 207 | 208 | # Backing up the directories to MinIo 209 | if [ $backup_to_minio_enable = "yes" ] 210 | if ! [ -x "$(command -v mc)" ]; then 211 | echo 'Error: minio client (mc) is not installed.' >&2 212 | exit 1 213 | fi 214 | then 215 | echo -e "\n ${color}--- $date_now Backing up directories \n${nc}" 216 | echo "$date_now Backing up directories" >> $log_file 217 | for backup_dirs in $minio_directories 218 | do 219 | echo "--> $backup_dirs" | tee -a $log_file 220 | dir_name=`echo $backup_dirs | awk -F'/' '{print $NF}'` 221 | mc mirror --overwrite $backup_dirs ${minio_cluster_name}/${minio_bucket}/${dir_name} 222 | done 223 | echo 224 | fi 225 | 226 | sleep 1 227 | 228 | 229 | # MySQL backup 230 | if [ $mysql_backup = "yes" ] 231 | then 232 | echo -e "\n ${color}--- $date_now MySQL backup enabled, backing up: \n${nc}" 233 | echo "$date_now MySQL backup enabled, backing up" >> $log_file 234 | # Using ionice for MySQL dump 235 | ionice -c 3 mysqldump -u $mysql_user -p$mysql_pass --events --all-databases | gzip -9 > $backup_path/Backup/$path_date/MySQL_Full_Dump_$path_date.sql.gz | tee -a $log_file 236 | if [ $? -eq 0 ] 237 | then 238 | echo -e "\n ${color}--- $date_now MySQL backup completed. \n${nc}" 239 | echo "$date_now MySQL backup completed" >> $log_file 240 | else 241 | echo -e " ${color_fail} MySQL backup failed. ${nc} \n" 242 | echo "$date_now MySQL backup failed" >> $log_file 243 | fi 244 | fi 245 | 246 | sleep 1 247 | 248 | # GitLab backup 249 | if [ $gitlab_backup = "yes" ] 250 | then 251 | echo -e "\n ${color}--- $date_now GitLab backup enabled, backing up: \n${nc}" 252 | echo "$date_now GitLab backup enabled, backing up" >> $log_file 253 | gitlab_backup_path=`grep 'backup_path' $gitlab_config | grep -v manage | cut -d "=" -f2 | cut -d '"' -f2` 254 | gitlab-rake gitlab:backup:create STRATEGY=${gitlab_mode} &> $log_file 255 | 256 | if [ $? -eq 0 ] 257 | then 258 | last_backup_file=`ls -ltr ${gitlab_backup_path} | awk '{print $9}' | tail -n 1` 259 | cp ${gitlab_backup_path}/${last_backup_file} $backup_path/Backup/$path_date/ 260 | 261 | echo -e "\n ${color}--- $date_now GitLab backup completed. \n${nc}" 262 | echo "$date_now GitLab backup completed" >> $log_file 263 | else 264 | echo -e " ${color_fail} GitLab backup failed. ${nc} \n" 265 | echo "$date_now GitLab backup failed" >> $log_file 266 | fi 267 | fi 268 | 269 | sleep 1 270 | 271 | # PostgreSQL backup 272 | if [ $postgres_backup = "yes" ] 273 | then 274 | # Creating ~/.pgpass for PostgreSQL password 275 | # PostgreSQL does not support inline password 276 | # Know better solution? Let me know. 277 | USERNAME=`whoami` 278 | CUR_DATE=$(date +"%Y-%m-%d-%H-%M-%S") 279 | if [ $USERNAME = "root" ] 280 | then 281 | echo "$postgres_host:$postgres_port:$postgres_database:$postgres_user:$postgres_pass" > /root/.pgpass 282 | chmod 600 /root/.pgpass 283 | else 284 | echo "$postgres_host:$postgres_port:$postgres_database:$postgres_user:$postgres_pass" > /home/$USERNAME/.pgpass 285 | chmod 600 /home/$USERNAME/.pgpass 286 | fi 287 | 288 | echo -e "\n ${color}--- $date_now PostgreSQL backup enabled, backing up: \n${nc}" 289 | echo "$date_now PostgreSQL backup enabled, backing up" >> $log_file 290 | # Using ionice for PostgreSQL dump 291 | ionice -c 3 pg_dump -p $postgres_port -h $postgres_host -Fc -U $postgres_user $postgres_database > ${backup_path}/Backup/${path_date}/Postgres_Full_Dump_${path_date}.dump | tee -a $log_file 292 | if [ $? -eq 0 ] 293 | then 294 | echo -e "\n ${color}--- $date_now PostgreSQL backup completed. \n${nc}" 295 | echo "$date_now PostgreSQL backup completed" >> $log_file 296 | fi 297 | fi 298 | 299 | sleep 1 300 | 301 | # MongoDB backup 302 | if [ $mongo_backup = "yes" ] 303 | then 304 | echo -e "\n ${color}--- $date_now MongoDB backup enabled, backing up: \n${nc}" 305 | echo "$date_now MongoDB backup enabled, backing up" >> $log_file 306 | # Using ionice for MongoDB dump 307 | ionice -c 3 mongodump --host $mongo_host --collection $mongo_collection --db $mongo_database --gzip --archive=${backup_path}/Backup/${path_date}/MongoDB_${mongo_collection}_${path_date}.dump | tee -a $log_file 308 | if [ $? -eq 0 ] 309 | then 310 | echo -e "\n ${color}--- $date_now MongoDB backup completed. \n${nc}" 311 | echo "$date_now MongoDB backup completed" >> $log_file 312 | fi 313 | fi 314 | 315 | sleep 1 316 | 317 | # Docker Backup 318 | # Mariadb or Mysql backup 319 | 320 | if [ $docker_mysql_backup = "yes" ] 321 | then 322 | echo -e "\n ${color}--- $date_now Docker Mariadb/MySQL backup enabled, backing up: \n${nc}" 323 | echo "$date_now Docker MySQL backup enabled, backing up" >> $log_file 324 | for docker_mysql_container in $docker_mysql_containers 325 | do 326 | docker_mysql_container_id=`echo $docker_mysql_container | awk -F":::" '{print $1}'` 327 | docker_mysql_container_name=`docker ps --filter "id=$docker_mysql_container_id" | awk '{print $11}'` 328 | docker_mysql_user=`echo $docker_mysql_container | awk -F":::" '{print $2}'` 329 | docker_mysql_pass=`echo $docker_mysql_container | awk -F":::" '{print $3}'` 330 | docker_mysql_database=`echo $docker_mysql_container | awk -F":::" '{print $4}'` 331 | docker exec $docker_mysql_container_id /usr/bin/mysqldump -u $docker_mysql_user --password=$docker_mysql_pass $docker_mysql_database | gzip -9 > $backup_path/Backup/$path_date/Docker_MySQL_${docker_mysql_container_name}_Dump_$path_date.sql.gz | tee -a $log_file 332 | if [ $? -eq 0 ] 333 | then 334 | echo -e "\n ${color}--- $date_now Docker Mariadb/MySQL backup completed. \n${nc}" 335 | echo "$date_now Backing up files" >> $log_file 336 | fi 337 | done 338 | fi 339 | 340 | 341 | ############################################################################################ 342 | 343 | # Create TAR file 344 | echo -e "\n ${color}--- $date_now Creating TAR file located in $backup_path/Full_Backup_$path_date.tar.bz2 \n${nc}" 345 | echo "$date_now Creating TAR file located in $backup_path/Full_Backup_$path_date.tar.bz2" >> $log_file 346 | tar -cjf $backup_path/Full_Backup_${path_date}.tar.bz2 $backup_path/Backup/$path_date 2> /dev/null 347 | rm -rf $backup_path/Backup/ 348 | final_archive="Full_Backup_${path_date}.tar.bz2" 349 | 350 | sleep 1 351 | 352 | ############################################################################################ 353 | 354 | # Encrypt using GPG 355 | if [ $gpg_enable = "yes" ] 356 | then 357 | echo -e "\n ${color}--- $date_now Encrypting archive file using $gpg_public_recipient key\n${nc}" 358 | echo "$date_now Encrypting archive file using $gpg_public_recipient key" >> $log_file 359 | gpg --yes --always-trust -e -r $gpg_public_recipient $backup_path/Full_Backup_${path_date}.tar.bz2 360 | # Removing the unencrypted archive file 361 | rm $backup_path/Full_Backup_${path_date}.tar.bz2 362 | final_archive="Full_Backup_${path_date}.tar.bz2.gpg" 363 | fi 364 | 365 | sleep 1 366 | 367 | # Copy to other storage 368 | if [ $external_copy = "yes" ] 369 | then 370 | for cp_paths in $external_storage 371 | do 372 | echo -e "\n ${color}--- $date_now Copy backup archive to $cp_paths: \n${nc}" 373 | echo "$date_now Copy backup archive to $cp_paths" >> $log_file 374 | cp $backup_path/$final_archive $cp_paths/ 375 | if [ $? -eq 0 ] 376 | then 377 | echo -e "Copied to $cp_paths. \n" 378 | echo "$date_now Copied to $cp_paths" >> $log_file 379 | else 380 | echo -e " ${color_fail} Copy to $cp_paths failed. ${nc} \n" 381 | echo "$date_now Copy to $cp_paths failed. Please investigate." >> $log_file 382 | fi 383 | done 384 | fi 385 | 386 | sleep 1 387 | 388 | # SCP to other server 389 | if [ $scp_enable = "yes" ] 390 | then 391 | echo -e "\n ${color}--- $date_now SCP backup archive to $scp_server: \n${nc}" 392 | echo "$date_now SCP backup archive to $scp_server" >> $log_file 393 | scp -P $scp_port $backup_path/$final_archive '$scp_username'@'$scp_server':$scp_path 394 | echo "$date_now SCP done" | tee -a $log_file 395 | fi 396 | 397 | sleep 1 398 | 399 | # Upload to FTP server 400 | if [ $ftp_enable = "yes" ] 401 | then 402 | if [ `which curl` ] 403 | then 404 | echo -e "\n ${color}--- $date_now Uploading backup archive to FTP server $ftp_server \n${nc}" 405 | echo "$date_now Uploading backup archive to FTP server $ftp_server" >> $log_file 406 | curl --connect-timeout 30 -S -T $backup_path/$final_archive ftp://$ftp_server/$ftp_path --user $ftp_username:$ftp_password | tee -a $log_file 407 | if [ $? -eq 0 ] 408 | then 409 | echo "$date_now FTP Upload Done" | tee -a $log_file 410 | else 411 | echo -e "\n ${color_fail}--- $date_now FTP upload failed. \n${nc}" 412 | echo "$date_now FTP upload failed. Please investigate." >> $log_file 413 | fi 414 | else 415 | echo -e " ${color_fail}--- $date_now You have been enabled FTP upload. ${nc}" 416 | echo -e " ${color_fail}--- $date_now You need to install curl package. ${nc}" 417 | echo -e " ${color_fail}--- $date_now FTP upload failed. ${nc}" 418 | echo "$date_now FTP upload failed. Install 'curl' package." >> $log_file 419 | fi 420 | fi 421 | 422 | # Upload archive file to MEGA.nz 423 | if [ $mega_enable = "yes" ] 424 | then 425 | if [ `which megaput` ] 426 | then 427 | echo -e "\n ${color}--- $date_now Uploading backup archive to MEGA.nz \n${nc}" 428 | echo "$date_now Uploading backup archive to MEGA.nz" >> $log_file 429 | megaput --reload --path $mega_path -u $mega_email -p $mega_pass $backup_path/$final_archive 430 | echo "$date_now MEGA Upload Done. Path: $mega_path" | tee -a $log_file 431 | else 432 | echo -e " ${color_fail}--- $date_now You have been enabled MEGA upload. ${nc}" 433 | echo -e " ${color_fail}--- $date_now You need to install megatools from http://megatools.megous.com ${nc}" 434 | echo -e " ${color_fail}--- $date_now MEGA upload failed. ${nc}" 435 | echo "$date_now Uploading to MEGA.nz failed. Install 'megatools' from http://megatools.megous.com" >> $log_file 436 | fi 437 | fi 438 | 439 | # Send a simple email notification 440 | if [ $send_email = "yes" ] 441 | then 442 | echo -e "Backup completed $date_now\nBackup path: $backup_path/$final_archive" | mail -s "Backup Result" $email_to >> $log_file 2>&1 443 | fi 444 | 445 | echo -e "\n" 446 | echo -e "###########################################################" 447 | echo -e "$date_now Backup finished" 448 | echo -e "Backup path: $backup_path/$final_archive" 449 | echo -e "###########################################################" 450 | echo -e "\n" 451 | echo "$date_now Backup finished. Backup path: $backup_path/$final_archive" >> $log_file 452 | echo "#######################" >> $log_file 453 | 454 | # Removing lock after successful backup 455 | rm /var/backup_lock 456 | 457 | exit 0 458 | --------------------------------------------------------------------------------