How To Back Up Forge WordPress Sites to Amazon S3

I’ve had my personal WordPress site and all of my clients’ sites hosted with Laravel Forge for a little over a year now. If you haven’t heard of Forge yet, you definitely need to check it out. Yes, it is mainly for PHP applications, but it will also host WordPress sites like a champ. Forge is great, but the one thing it lacks is a way to back up your sites.

I wrote a script to handle backing up each WordPress site to Amazon S3 for safe-keeping as a part of my own backup plan.

Directory Structure

Before walking through the script, I want to take a moment to outline the directory structure. I’ve created a backups folder where all of this lives.

backups
|--- .env
|--- backup.sh
|--- client-sites/
|--- logs/
|    |--- 09-2018/
|    |--- 10-2018/
|--- sites

Within the backups directory, there’s an .env I added so the mysql\_user and password are not embedded directly in the script.

The client-sites/ folder is where the script does all of the file work — things like copying current site files and compressing them happen there.

The logs/ folder is just a place to store all of the logs for the backup processes.

Finally, the sites file which lists out all the databases and domains of the sites that are hosted on the server. Don’t get hung up on the fact that it’s using the domains. By default, Forge creates each site in its own home directory named after the domain of the site.

Here is what the sites file could look like:

Here’s the entire script:

Walking Through The Script

The whole first part of this script is the actual do_backup() function which handles the backup process. If you skip down towards the bottom of the file, you’ll see a couple of loops:

This section of the script is responsible for actually kicking off the backup function for each line in the sites file. We loop through the file and do an action for each line. While we’re working with that line we change the [Internal Field Separator] to break up the line by spaces into two parts.

The next line then kicks off the backup function passing the two parts as parameters. The first one passed ($1) will be the database name for the site and the second ($2) will be the home directory where the site lives.

Here’s an example of what that line would look like if we could step through the execution of the loop:

$ do_backup trecord staging.tannerrecord.com

The backup function looks like it does a lot, but really only does 3 things:

  • Copy the current site files into client-sites/
  • Compress the copied site files into something like staging.tannerrecord.com_2018_10_30.tar.gz
  • Upload the compressed file to Amazon S3

The beginning of the function is just defining some variables and outputting a nice header to the log file.

Next, it copies the current site files into the client-sites/ folder so it’s not working directly on the actual live site files.

Next, it dumps the mysql database into the copy of the site files.

Compresses the file:

When uploading to S3, you will need to have awscli installed on the server for this script to work. You can install it with pip through ssh with: $ pip install awscli

The --no-progress parameter just suppresses some of the extra information that shouldn’t be included in the log file.

In conclusion, it only took a few hours to develop this backup script and most of that time was actually debugging some issues with an old version of awscli.

Before I wrote this script, I was paying for a backup service that was $50 a month (and that only includes 5GB of storage). When all of my sites back up, they end up being about 2GB total and I’m going to keep 30 days of backups. When you look at the pricing of S3 ($0.023 per GB), which amounts to $1.38/mo, it’s not very hard to see why I wrote this script. I hope that it helps you get a better understanding of bash scripts and the power of doing some things yourself.