WordPress backups to a remote server

Earlier, I used a WordPress plugin to do periodic backups of the sites I’m hosting, but I came to a conclusion that it would be more reliable, flexible and fun to do the backups via a system script instead, sending the resulting backup archives to a remote server. Here’s how I ended up doing it:

First, I wanted to create a separate mariadb/mysql user with only just enough privileges to be able to make a mysqldump (using the “Principle of Least Privilege“; for the same reason the backup script is executed by an user, not by root). I chose the username ‘dumpo’ because… well. he just dumbly dumps stuff:

MariaDB [(none)]> CREATE USER 'dumpo'@'localhost' IDENTIFIED BY '<dumpopasswd>';
MariaDB [(none)]> GRANT SELECT, SHOW VIEW, LOCK TABLES, RELOAD, REPLICATION CLIENT ON *.* TO 'dumpo'@'localhost';
MariaDB [(none)]> FLUSH PRIVILEGES;

Then, the mysqldump user’s credentials must be stored in the ~/.my.cnf file within the backup-executing user’s home directory. Afterwards, run chmod 600 ~/.my.cnf to set the correct permissions.

  1. [mysqldump]
  2. user=dumpo
  3. password=<dumpopasswd>

The actual backup script below is executed by an user’s cronjob once a week. I added a lot of comments to the example script to explain how it works.

  1. #!/bin/bash
  2.  
  3. # The device acting as a fileserver for WP backups is not always powered up,
  4. # so let's use Wake-On-LAN to make sure it is up when it is actually needed.
  5. wol <mac-address-of-fileserver>
  6.  
  7. # I happen to have the same username on both the webserver and the fileserver,
  8. # that simplifies some settings.
  9. USER=<user-running-the-script>
  10. WPUSER=dumpo
  11. BFILENAME=backupwp-$(date +%d%m%Y)
  12. # I'm mailing the backup messages somewhere.
  13. MAILADR=someone@somewhere.com
  14.  
  15. # Let's make a temporary working directory on the users home directory.
  16. cd /home/${USER}
  17. mkdir -p backupdb
  18.  
  19. # I have three Wordpress sites on the server.
  20. # Dump the mariadb databases of those sites to the working directory.
  21. mysqldump -u ${WPUSER} SITE1 > backupdb/SITE1.sql
  22. mysqldump -u ${WPUSER} SITE2 > backupdb/SITE2.sql
  23. mysqldump -u ${WPUSER} SITE3 > backupdb/SITE3.sql
  24. mysqldump -u ${WPUSER} mysql > backupdb/MYSQL.sql
  25. echo -e "Database dump: done"
  26.  
  27. # Copy the Wordpress installations to the working directory.
  28. cp -a /var/www/site1.com backupdb/
  29. cp -a /var/www/site2.com backupdb/
  30. cp -a /var/www/site3.com backupdb/
  31. echo -e "Website copy: done"
  32.  
  33. # Make a tar.gz archive of the contents of the working directory.
  34. tar -czf ${BFILENAME}.tar.gz backupdb
  35. echo -e "Archive: done"
  36.  
  37. # Check whether the fileserver is up and running. I'm checking the port 22
  38. # (ssh) because I'll be using rsync with ssh for upload. The loop waits for
  39. # 5 minutes for the connection, and then gives up and mails an error message.
  40. # Waiting for 5 minutes may actually be useless, because the fileserver usually
  41. # boots up within a minute, and making the backup archive already takes up some
  42. # 3-4 minutes. But just in case...
  43. CHECK=0
  44. until [[ `nmap <fileserver-ip-addr> -p 22 | grep "Host is up"` ]]
  45. do
  46.     if [ $CHECK -ge 60 ]; then
  47.         echo -e "File server unreachable, aborting!!! $(date '+%d-%m-%Y %T')" | mail -s "Backup" ${MAILADR}
  48.         exit 1
  49.     fi
  50.     sleep 5
  51.     let CHECK++
  52. done
  53.  
  54. # Send the backup archive to the fileserver using rsync with ssh.
  55. rsync -e ssh ${BFILENAME}.tar.gz ${USER}@<fileserver-ip-addr>:/home/${USER}/wpbackup/
  56.  
  57. # If the upload was succesful, clean up by removing the local archive and
  58. # temporary  working directory, and mail a success message; otherwise, leave
  59. # everything as it is and mail a failure message.
  60. if [[ $? == 0 ]]; then
  61.     rm ${BFILENAME}.tar.gz
  62.     rm -rf backupdb
  63.     echo -e "Wordpress and mariadb backup completed successfully at $(date '+%d-%m-%Y %T')" | mail -s "Backup" ${MAILADR}
  64. else
  65.     echo -e "Archive upload failure!!! $(date '+%d-%m-%Y %T')" | mail -s "Backup" ${MAILADR}
  66.     exit 1
  67. fi

Finally, at the fileserver end, a script – executed by the cron some time after the upload – rotates and cleans up the backups, leaving only the 4 latest backup archives:

  1. #!/bin/bash
  2.  
  3. cd /home/<user>/wpbackup
  4. echo -e "Rotating Wordpress backups, removing following files:\n"
  5. ls -1t | tail -n +5 | xargs rm -v # That is 'ls dash one t'.
This entry was posted in Linux and tagged , , , , . Bookmark the permalink.