Skip to content

Instantly share code, notes, and snippets.

@alainwolf
Last active May 10, 2025 16:34
Show Gist options
  • Save alainwolf/147ae04d91f29655d317af5656ccf85f to your computer and use it in GitHub Desktop.
Save alainwolf/147ae04d91f29655d317af5656ccf85f to your computer and use it in GitHub Desktop.
Website Backup (Hosttech, Plesk, Wordpress and others)
#!/usr/bin/env bash
# ****************************************************************************
# Website backup for Webservers
# - Set Wordpress into maintenance mode
# - Create CACHE_DIR.TAG files to exclude cache directories
# - Dump databases into temporary files
# - Create archive of all files incl. the database dump
# - Transfer archive to remote backup storage
# - Send health check notifications to https://healthchecks.io
# - Clean-up temporary files
#
# Edited by Alain Wolf on Fri, 9. May 2025 15:09
# ****************************************************************************
# --------------------------------------------------
# Settings
# --------------------------------------------------
# Read settings from conf file
# shellcheck source=website-backup.conf.sample
. "${0}.conf"
# Fail on error or unset variable
set -e -u
# Fail on pipe error
set -o pipefail
# Logging function
log() {
echo "$(date +'%Y-%m-%d %H:%M:%S') - $1"
}
log "Starting backup process"
# Send health check notification
if [ -n "${healthcheck_uuid}" ]; then
log "Sending health check notification to https://healthchecks.io"
curl -sS --retry 3 --retry-delay 5 --retry-max-time 30 \
"https://hc-ping.com/${healthcheck_uuid}/start"
fi
# Create CACHE_DIR.TAG files to exclude cache directories
# https://bford.info/cachedir/
for CACHE_DIR in ${CACHE_DIRS}; do
# Check if the directory exists
if [ ! -d "${CACHE_DIR}" ]; then
log "Cache directory ${CACHE_DIR} does not exist, skipping."
continue
else
# Check if a CACHE_DIR.TAG file already exists
if [ ! -f "${CACHE_DIR}/CACHEDIR.TAG" ]; then
log "Creating CACHEDIR.TAG file in ${CACHE_DIR}"
echo "Signature: 8a477f597d28d172789f06886806bc55" >"${CACHE_DIR}/CACHEDIR.TAG"
fi
fi
done
# Archive filename
ARCHIVE_NAME="backup-$(date +%Y-%m-%d_%H-%M-%S).tar.gz"
# Create temporary directory
TMP_DIR=$(mktemp -d)
log "Created temporary directory ${TMP_DIR}"
# Put Wordpress site in maintenance mode
# https://developer.wordpress.org/reference/functions/wp_is_maintenance_mode/
if [ -n "${WP_DIR}" ]; then
log "Enabling Wordpress maintenance mode."
# shellcheck disable=SC2016
echo '<?php $upgrading = time(); ?>' >"${WP_DIR}/.maintenance"
fi
# Let things settle
sleep 5
# Dump databases
mkdir -p "${TMP_DIR}/SQL"
for DB in ${DB_DATABASES}; do
log "Dumping database ${DB}"
/usr/bin/mariadb-dump \
--host="${DB_HOST}" --port="${DB_PORT}" --user="${DB_USER}" --password="${DB_PASSWORD}" \
--add-drop-database --add-locks --comments --create-options --dump-date \
--flush-privileges --single-transaction --routines \
--databases "${DB}" >"${TMP_DIR}/SQL/${DB}.sql"
done
# Let things settle
sleep 5
# Create archive
log "Creating archive ${ARCHIVE_NAME}"
# shellcheck disable=SC2046
tar --create --gzip --file "${TMP_DIR}/${ARCHIVE_NAME}" \
--exclude-caches --exclude-caches-under --exclude-tag=.nobackup \
--directory "${HOME}" $(for dir in ${BACKUP_DIRS}; do echo "${dir}"; done) \
--directory "${TMP_DIR}" SQL
# Disable Wordpress maintenance mode
if [ -n "${WP_DIR}" ]; then
log "Disabling Wordpress maintenance mode."
rm -f "${WP_DIR}/.maintenance"
fi
# Transfer archive
log "Transferring archive to ${REMOTE_HOST}:${REMOTE_PATH}"
rsync --times "${TMP_DIR}/${ARCHIVE_NAME}" "${REMOTE_HOST}:${REMOTE_PATH}/"
# Clean-up
log "Cleaning up temporary files"
rm -rf "${TMP_DIR}"
# Send health check notification
if [ -n "${healthcheck_uuid}" ]; then
log "Sending health check notification to https://healthchecks.io"
curl -sS --retry 3 --retry-delay 5 --retry-max-time 30 \
"https://hc-ping.com/${healthcheck_uuid}"
fi
log "Backup process completed"
#!/usr/bin/env bash
# shellcheck disable=SC2034
# ****************************************************************************
# Website backup configuration settings for example.org
#
# Edited by Alain Wolf on Fri, 10. February 1967 09:01
# ****************************************************************************
# --------------------------------------------------------
# Configuration Settings
# --------------------------------------------------------
# Wordpress install directory
# If set, the script will put Wordpress into maintenance mode during the backup
#WP_DIR='/httpdocs'
# Database server connection
DB_HOST='localhost'
DB_PORT='3306'
DB_USER='CHANGEME'
DB_PASSWORD='CHANGEME'
DB_DATABASES='
wp_database
'
# Directories (and files) to backup (database dumps are always included)
BACKUP_DIRS='
httpdocs
.local
.ssh
error_docs
logs
mta-sts
'
# Cache directories to exclude from backups
CACHE_DIRS='
httpdocs/wp-content/cache
httpdocs/wp-content/et-cache
httpdocs/wp-content/updraft
'
# Remote backup storage
# SSH connection and login settings are exptected to be set in ~/.ssh/config
REMOTE_HOST='mynas.exaple.com'
REMOTE_PATH='/volume1/Backups/Hosting/Server/Website'
# Health check UUID
# If set, the script will send status updates to https://healthchecks.io
#healthcheck_uuid='aaaaaaaa-bbbb-cccc-1234-dddddddddddd'
# -*- mode: bash; indent-tabs-mode: nil; tab-width: 4; -*-
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment