tl;dr
This post documents how I migrated my managed wordpress instances to my own VPS using PHP-FPM and NGINX in case I need to do it again!
Introduction
Some years ago, I got an offer for managed wordpress that I could not refuse: unlimited instances, disk space, traffic, databases, etc.
I was fairly happy until I wanted to share a MySQL database outside the service provider’s private network.
Well, I couldn’t, because I didn’t have access rights to configure the firewall.
So I ordered the cheapest SSD VPS by contabo as an alternative.
I chose PHP-FPM for security reasons, e.g., containing script execution to a single user.
NOTE: WP provides guidance on how to migrate a WP instance.
Here, following assumptions are made:
- A single WP site is being migrated
- Domain name stays the same
SSH access to server is given
Prepare new server
For a debian 10, that I’m using, at least the following packages need to be installed:
apt install \
php7.3-fpm \
php7.3-mysql \
nginx \
mariadb-common
Backing up old instance
Although there’s an official WP guide on how to backup data, I wanted to have it automated and uncomplicated.
The result was a single bash script wp-backup.sh:
#!/bin/bash
set -euo pipefail
err() {
>&2 echo "$1"
exit 1
}
main() {
if [[ ! -d "$1" ]]; then
err "Given path ($1) not found or not a directory!"
fi
local readonly WPCONFIG="$1/wp-config.php"
if [[ ! -r "${WPCONFIG}" ]]; then
err "WP config file (${WPCONFIG}) not found or not readable!"
fi
local readonly DEFINERE="^define\('(.*)'[[:blank:]]*,[[:blank:]]*'(.*)'\)"
local line
declare -A configValues
while read -r line; do
if [[ "${line}" =~ ${DEFINERE} ]]; then
local key="${BASH_REMATCH[1]}"
local value="${BASH_REMATCH[2]}"
configValues[${key}]="${value}"
fi
done < "${WPCONFIG}"
# Serialize config (for restore script)
local configTarget="config.sh"
echo "Dumping WP config to '${configTarget}'"
declare -p configValues > "config.sh"
# Backup DB
local sqlTarget="${configValues[DB_NAME]}.sql"
echo "Dumping sql data to '${sqlTarget}'"
mysqldump --host="${configValues[DB_HOST]}" --user="${configValues[DB_USER]}" --password="${configValues[DB_PASSWORD]}" "${configValues[DB_NAME]}" > "${sqlTarget}"
# Backup WP files
local wpTarget="${configValues[DB_NAME]}.gz"
echo "Dumping WP files to '${wpTarget}'"
tar -czf "${wpTarget}" "$1"
}
main "$@" || exit 1
Now you can backup as follows:
# Log on to your server
# copy wp-script.sh over
mkdir backup && cd backup
./PATH/TO/wp-script.sh PATH/TO/WP-DIR
Restoring
Log on to your new server and scp the backup data from previous step over into the home directory of created user:
scp -r OLD_SERVER:/PATH/TO/backup /home/example_com
To quickly restore DB and WP files as well as configuring NGINX and PHP-FPM, wp-restore.sh can be used:
#!/bin/bash
set -eo pipefail
err() {
>&2 echo "$1"
exit 1
}
main() {
if [[ -z "$1" ]]; then
err "Host name not given or empty!"
fi
if [[ ! -d "$2" ]]; then
err "Backup directory '$2' is invalid!"
fi
local readonly SITENAME="$1"
local readonly SITESAFE=$(echo "${SITENAME}" | tr '.' '_')
local readonly WPBACKUP=$(find "$2" -type f -name "*.gz" | head -n 1)
local readonly DBBACKUP=$(find "$2" -type f -name "*.sql" | head -n 1)
local readonly TARGETBASE="/home/${SITESAFE}"
local readonly WPTARGET="${TARGETBASE}/${SITENAME}"
# Load WP configs
source "$2/config.sh"
# Create user
if ! id -u "${SITESAFE}" > /dev/null; then
adduser --system --group "${SITESAFE}"
fi
# Extract WP files
mkdir -p "${WPTARGET}"
tar -xf "${WPBACKUP}" --directory "${WPTARGET}" --strip-components 1
chown -R "${SITESAFE}":"${SITESAFE}" "${WPTARGET}"
# Setup and restore DB
echo "Setting up database"
mysql -u root -p <<- SQL
DROP USER IF EXISTS '${configValues[DB_USER]}'@'localhost';
CREATE USER '${configValues[DB_USER]}'@'localhost' IDENTIFIED BY '${configValues[DB_PASSWORD]}';
DROP DATABASE IF EXISTS ${configValues[DB_NAME]};
CREATE DATABASE ${configValues[DB_NAME]};
GRANT ALL ON ${configValues[DB_NAME]}.* TO '${configValues[DB_USER]}'@'localhost';
USE ${configValues[DB_NAME]};
SOURCE ${DBBACKUP};
SQL
# Setup PHP-FPM
echo "Setting up PHP FPM"
cat <<- CONF > /etc/php/7.3/fpm/pool.d/${SITESAFE}.conf
[${SITESAFE}]
user = ${SITESAFE}
group = ${SITESAFE}
listen = /run/php/php7.3-fpm-${SITESAFE}.sock
listen.owner = www-data
listen.group = www-data
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,dl,setenv
php_admin_flag[allow_url_fopen] = off
; Choose how the process manager will control the number of child processes.
pm = dynamic
pm.max_children = 75
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.process_idle_timeout = 10s
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
CONF
if php-fpm7.3 -t; then
service php7.3-fpm restart
else
err "Something went wrong with PHP FPM!"
fi
# Setting up NGINX
echo "Setting up NGINX"
local readonly NGINXCONF="/etc/nginx/sites-available/${SITENAME}"
cat <<- CONF > "${NGINXCONF}"
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
server_name ${SITENAME} www.${SITENAME};
include snippets/wordpress.conf;
root ${WPTARGET};
access_log /var/log/nginx/${SITENAME}-access.log;
error_log /var/log/nginx/${SITENAME}-error.log error;
index index.php;
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.3-fpm-${SITESAFE}.sock ;
}
ssl_protocols TLSv1.2 TLSv1.3;
ssl_stapling on;
ssl_stapling_verify on;
}
CONF
ln -s "${NGINXCONF}" /etc/nginx/sites-enabled
if nginx -t; then
service nginx restart
else
err "Something went wrong while configuring NGINX"
fi
}
main "$@" || exit 1
Restoring is as easy as:
./PATH/TO/wp-restore.sh example.com PATH/TO/BACKUP
This script changes the system as follows:
- A system user corresponding to given domain name is generated, e.g.,
example_com.
- Compressed WP files are extracted to a directory under home directory of the newly created user, e.g.,
/home/example_com/example.com
- A new SQL user is added and is filled with the previously created dump
- A PHP-FPM configuration file is generated and activated
- A new NGINX block is generated and activated
Finishing touches
You’re not done before you get some fresh new certificates for your wordpress instance.
Go ahead and install certbot and run the following:
certbot --nginx\
-d example.com -d www.example.com\
--server 'https://api.buypass.com/acme/directory'
This would fetch a new certificate from buypass, a european alternative to Let’s Encrypt, and automatically configure your nginx block.
|