Using tar and gzip to create file backups
In this lesson you will create compressed, timestamped backups of your WordPress
files using tar and gzip. You'll decide what's worth backing up and what to
skip, then write a script that archives every site on your server.
tar bundles a whole
directory tree into a single archive file, and gzip compresses it. They're two
separate tools that are almost always used together, and you've probably run into
a .tar.gz file before. We'll end up with one per site, much like the compressed
database dumps from the mysqldump lesson.
Files vs uploads
A WordPress install is made up of a few different kinds of files. There's the
WordPress core, the plugins and themes, and then the uploads and other files in
wp-content that are generated as the site is used.
Much of this is reproducible. Core, plugins and themes can be reinstalled, and a lot of our configuration already lives in the config repository. The uploads directory, however, is usually the one irreplaceable part, and often the largest.
You could back up wp-content/uploads on its own to save space, but I prefer to
archive the entire document root. It captures anything a plugin might have
dropped somewhere unexpected, and it means a single file holds everything I need
to bring a site back. As with database backups, I'd rather have one complete
copy per site.
Excluding caches
Not everything in the document root is worth archiving. Page and object caches,
for example, are regenerated automatically and only bloat our backups. We can
leave them out with the --exclude option:
tar -czf backup.tar.gz --exclude='wp-content/cache' \
-C /sites/uncached.org/public_html .
You can add as many --exclude options as you need, one for each path you want
to leave out.
Preserving ownership
This is another reason we run our backups as root. In our setup the site files
are mostly owned by www-data, and tar records that ownership inside the
archive as it reads each file. On a restore, extracting as root puts the files
back under their original owner, so a site comes back exactly as it was.
The files backup script
You can find the full up to date script in the config repository on GitHub, but I will explain some of the logic used to write that script here. It follows the same pattern as our database backup script.
We start by generating a timestamp, then loop through all the public_html
directories in the /sites/ directory, skipping anything that isn't a WordPress
site:
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
for docroot in /sites/*/public_html; do
[ -d "$docroot" ] || continue
[ -f "$docroot/wp-config.php" ] || continue
Next we work out the site name and prepare a destination directory and file for
it under /backups:
site=$(basename "$(dirname "$docroot")")
dest_dir="/backups/$site"
mkdir -p "$dest_dir"
dest_file="$dest_dir/${TIMESTAMP}.tar.gz"
Finally we create the archive, excluding our caches. We check tar's exit
status by hand, because a file simply changing while it's read is only a warning,
not a reason to throw the backup away:
echo "[$site] archiving files to $dest_file"
tar -czf "$dest_file" --exclude='wp-content/cache' \
-C "$docroot" .
status=$?
if [ "$status" -eq 0 ]; then
echo "[$site] done"
elif [ "$status" -eq 1 ]; then
echo "[$site] some files changed while archiving, keeping backup" >&2
else
echo "[$site] tar failed" >&2
rm -f "$dest_file"
fi
done
The tar options are -c to create an archive, -z to compress it with
gzip, and -f to write it to a file. The -C option changes into the
document root before archiving the . directory, so the paths stored inside the
archive are relative to the site root, rather than starting with /sites/....
I called this script backup-files.sh and placed it in the /config/bin/
directory. Like our database script, it needs to run as root to read every
site's files and record their ownership. To invoke it as my karl user I'll
need sudo:
sudo /config/bin/backup-files.sh
ls -lh /backups/uncached.org/
Our /backups/uncached.org/ directory now holds a timestamped .tar.gz
archive, right alongside the .sql.gz database dumps from the previous lesson.
Since all of this lives under /backups, it's already protected by the 0700
permissions we set on that directory earlier, so only root can access it.
This article is for premium members only. One-time payment of $196 unlocks lifetime access to all existing and future content on wpshell.com, and many other perks.