There is a crazy amount of WordPress backup plugins out there. One of my favorites is UpdraftPlus available on the repository for when I have to move sites between shared hosting accounts. When I back up my own sites from my VPS, I replicate the behavior of many plugins: package up the whole site, database and send it to Cloud Storage. This is lighter on resources and isn’t prone to PHP timeouts or other nonsense.
In this tutorial I will be using WP-CLI and bash to back up WordPress or WooCommerce sites (entire folder and database) and upload those packages to Dropbox from your VPS or dedicated server. We’ll also be deleting old backups too.
Note: Dropbox provides 2 GB of space for free, if your site is larger consider using Google Drive instead with this guide.
Update: bugfix to delete old scripts and $SITENAME
variable (thanks Ahmad)
Automatically Back up WordPress to Dropbox with WP-CLI and Bash
Installation overview
- Create the Dropbox app for the access token
- Test the bash Dropbox uploader
- Create the Backup script
- Schedule the backup script to run daily
Create Dropbox App
Log in to the Dropbox Developer section
Under My apps click Create app.
Under Choose an API, select Dropbox API.
Under Choose the type of access you need, select App folder.
Name your app whatever you want. This will become the folder name in your Dropbox folder.
Check I agree and click Create app.
Now we can generate the access token that the Dropbox Uploader bash program needs.
Under the OAuth2 section, click Generate.
You will need this access token for the Dropbox Uploader.
Install Dropbox Uploader
The Dropbox Uploader requires curl so make sure it is installed.
sudo apt-get update
sudo apt-get install curl -y
Now download the dropbox_uploader
and place it into /usr/bin
.
sudo curl "https://raw.githubusercontent.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh" -o /usr/bin/dropbox_uploader
If you want to run dropbox_uploader
as a regular user
you can change the ownership of the program to that user.
sudo chown user /usr/bin/dropbox_uploader
Make sure the dropbox_uploader
allows its owner to read, write and execute (7)
sudo chmod 755 /usr/bin/dropbox_uploader
Now you can start the initial setup wizard by executing
dropbox_uploader
When you see this prompt, paste your Dropbox Access token created earlier.
This is the first time you run this script, please follow the instructions:
1) Open the following URL in your Browser, and log in using your account: https://www.dropbox.com/developers/apps
2) Click on "Create App", then select "Dropbox API app"
3) Now go on with the configuration, choosing the app permissions and access restrictions to your DropBox folder
4) Enter the "App Name" that you prefer (e.g. MyUploader267311959129698)
Now, click on the "Create App" button.
When your new App is successfully created, please click on the Generate button
under the 'Generated access token' section, then copy and paste the new access token here:
# Access token: w4xJFA74LXAAAAAAAAABZDI7k7NZSYrk1yvAynXzQTv4mHA8uMuYvIB73fa9MgZo
Confirm it’s all good by pressing y
The access token is w4xJFA74LXAAAAAAAAABZDI7k7NZSYrk1yvAynXzQTv4mHA8uMuYvIB73fa9MgZo. Looks ok? [y/N]: y
The configuration has been saved.
Your Access token can now be found in your home folder’s hidden file ~/.dropbox_uploader.
Time to test the Dropbox Uploader.
Test Dropbox Uploader
Enter the temorary directory and create a test file
cd /tmp
echo "test" > testfile.txt
Upload that test file to Dropbox
dropbox_uploader upload testfile.txt /
> Uploading "/tmp/testfile.txt" to "/testfile.txt"... DONE
You can verify that testfile.txt is there by checking the Dropbox App folder.
Now let’s delete the testfile.txt since we know it works
dropbox_uploader delete testfile.txt /
> Deleting "/testfile.txt"... DONE
If you want a quick way to see all the files in the App folder this command will help
dropbox_uploader list / | awk '{print $3}'
"/"...
testfile.txt
To get all the directories
dropbox_uploader list / | awk '{print $2}'
Listing
guides.wp-bullet.com
Now we can use this along with WP-CLI to back up WordPress and WooCommerce sites.
Create the Dropbox Backup Script
Install WP-CLI and make the user
own it with the executable permissions.
sudo wget -q https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar -O /usr/bin/wp
sudo chown user /usr/bin/wp
sudo chmod 755 /usr/bin/wp
Create the Dropbox backup script
nano ~/scripts/dropboxbackup.sh
Paste this script which backs up WordPress with tar, the database with wp-cli (and gzips it), then uploads the packages to Dropbox.
It will go through all WordPress folders in the SITESTORE
path.
The script also deletes old backups based on the DAYSKEEP
variable from both the local system and Dropbox.
At the end the script will fix any permissions, if you are on a non-standard Debian/Ubuntu setup that doesn’t use the www-data
user you should comment these lines out with #
#!/usr/bin/env bash
# Source: https://guides.wp-bullet.com
# Author: Mike
#define local path for backups
BACKUPPATH=/tmp/backups
#path to WordPress installation folders
SITESTORE=/var/www
#date prefix
DATEFORM=$(date +"%Y-%m-%d")
#Days to retain
DAYSKEEP=7
#calculate days as filename prefix
DAYSKEPT=$(date +"%Y-%m-%d" -d "-$DAYSKEEP days")
#create array of sites based on folder names
SITELIST=($(ls -d $SITESTORE/* | awk -F '/' '{print $NF}'))
#make sure the backup folder exists
mkdir -p $BACKUPPATH
#start the loop
for SITE in ${SITELIST[@]}; do
#check if there are old backups and delete them
EXISTS=$(dropbox_uploader list /$SITE | grep -E $DAYSKEPT.*.tar.gz | awk '{print $3}')
if [ ! -z $EXISTS ]; then
dropbox_uploader delete /$SITE/$DAYSKEPT-$SITE.tar.gz /$SITE/
dropbox_uploader delete /$SITE/$DAYSKEPT-$SITE.sql.gz /$SITE/
fi
echo Backing up $SITE
#enter the WordPress folder
cd $SITESTORE/$SITE
if [ ! -e $BACKUPPATH/$SITE ]; then
mkdir $BACKUPPATH/$SITE
fi
#back up the WordPress folder
tar -czf $BACKUPPATH/$SITE/$DATEFORM-$SITE.tar.gz .
#back up the WordPress database
wp db export $BACKUPPATH/$SITE/$DATEFORM-$SITE.sql --all-tablespaces --single-transaction --quick --lock-tables=false --allow-root --skip-themes --skip-plugins
cat $BACKUPPATH/$SITE/$DATEFORM-$SITE.sql | gzip > $BACKUPPATH/$SITE/$DATEFORM-$SITE.sql.gz
# remove the uncompressed sql file
rm $BACKUPPATH/$SITE/$DATEFORM-$SITE.sql
#upload packages
dropbox_uploader upload $BACKUPPATH/$SITE/$DATEFORM-$SITE.tar.gz /$SITE/
dropbox_uploader upload $BACKUPPATH/$SITE/$DATEFORM-$SITE.sql.gz /$SITE/
done
#if you want to delete all local backups
#rm -rf $BACKUPPATH/*
#delete old backups locally over DAYSKEEP days old
find $BACKUPPATH -type d -mtime +$DAYSKEEP -exec rm -rf {} \;
#Fix permissions for standard Debian and Ubuntu installations
sudo chown -R www-data:www-data $SITESTORE
sudo find $SITESTORE -type f -exec chmod 644 {} +
sudo find $SITESTORE -type d -exec chmod 755 {} +
I recommend testing the WordPress Dropbox backup script
sudo bash ~/scripts/dropboxbackup.sh
Now add the script as a cronjob so it runs every day
crontab -e
Enter this so the script runs at midnight daily, you may want to use absolute paths to the script (e.g. /home/user/scripts or /root/scripts)
@daily /bin/bash ~/scripts/dropboxbackup.sh
Ctrl+X, Y and Enter to Save and Exit
Now every day the Dropbox backup script will run exporting your database with WP-CLI, your WordPress installation with tar and upload them to Dropbox without any plugins while deleting old backups!
Great tutorial, will set up this soon 🙂
btw. you have a text inside your second code block.
https://uploads.disquscdn.com/images/422be5b8530520491b03ddb2e2f2629051793605d6a670e56ed2b42883dcbca9.png
Thank you CroModder, will sort that out 🙂
btw I found a bug in the script so it doesn’t delete old files (the EXISTS variable turns into an array)
This seems to solve the problem
EXISTS=$(dropbox_uploader list /$SITE | grep -E $DAYSKEPT.*.tar.gz | awk ‘{print $3}’)
Nice.
I got an idea about syncing the local files with the dropbox. We could take advantage of the metadata(https://www.dropbox.com/developers/reference/migration-guide#metadata-endpoints ) and see if the backup is needed. Thought about that to spare the write operations to dropbox. What do you think about that? Is it something worth looking, or just unnecessary complication? 🙂
That would be cool, there is definitely a use case for it especially for larger sites that don’t necessarily change that much. I am just busy atm preparing other guides (there are plenty more coming!) so I don’t have too much time to look at it. I would love to see an implementation on it if you are working on one and help out if I can.
I also don’t have time at the moment, but if I decide to implement this in the future will post it here.
And looking forward to the new guides 🙂
There’s an issue in there. $SITENAME is not defined anywhere.
Good catch Ahmad! I was leftover from testing, has now been removed.
Glad I could help 🙂