Set up a WordPress test site in 60 seconds using Bash Script
Have you ever struggled with a new WordPress site configuration? A new subdomain setup, generating the SSL certificate, creating an SSH account, GIT deploy keys setup, copying new WordPress files etc. All of this seems like a lot of work – work that can be automated.
It is crucial to have a test page (staging) when building a new WordPress website. In this tutorial, we will show how to use a single Bash script that will handle all of those tasks in a matter of seconds.
The Apache server uses FPM and has multiple PHP versions installed. Once the script is executed, we will see a wizard that includes questions about the domain name, PHP version and GIT repository address.
We assume that you have root access to the server and Apache installed. With small modifications, the script can also be used with NGINX.
Apache2 configuration
The first part is responsible for Apache server and PHP configuration. We’re creating the Home directory, a Linux user, applying correct permissions and owners of directories, creating an SSH connection, creating the virtualhost file, a directory with apache logs and a PHP FPM setup.
Creating the MYSQL database
The database will be created automatically with a random password generated. For this to work, your root MYSQL password needs to be hardcoded in the .sh file → replace YOUR_MYSQL_PASSWORD with your password.
echo -n "Creating database for user "$USERNAME"...";MYSQL=`which mysql`;
PASSWORD=`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 10 ; echo ''`
Q1="CREATE DATABASE IF NOT EXISTS "$DOMAIN"_db;"
Q2="GRANT ALL ON *.* TO '$USERNAME'@'localhost' IDENTIFIED BY '$PASSWORD';"
Q3="FLUSH PRIVILEGES;"
SQL="${Q1}${Q2}${Q3}"
$MYSQL -uroot -pYOUR_MYSQL_PASSWORD -e "$SQL"
echo -n "Database "$DOMAIN"_db created";
Gitlab deploy keys
Our script deploys keys to the GITlab repo. This enables the ability to update the test server using one simple command: git pull. Make sure to replace YOUR_PRIVATE_TOKEN with a real value configured in your gitlab settings.
echo -n "Now, we've deploy keys to gitlab.";
PUBLIC_KEY=`cat $HOME/.ssh/id_rsa.pub`;
ssh-keyscan gitlab.YOUR_COMPANY.com >> $HOME/.ssh/known_hosts
PROJECT_ESCAPED=`rawurlencode $PROJECT_NAME`;
echo -n "Adding deploy keys";
curl --request POST --header "PRIVATE-TOKEN: YOUR_PRIVATE_TOKEN" --header "Content-Type: application/json" --data '{"title": "'$DOMAIN' dev.YOUR_COMPANY.com", "key": "'"$PUBLIC_KEY"'", "can_push": "false"}' https://gitlab.YOUR_COMPANY.com/api/v4/projects/$PROJECT_ESCAPED/deploy_keys;
echo -n "Public keys deployed";
SSL certificate
We’re going to use certbot installed on Linux to create a free Let’s Encrypt SSL certificate. This will ensure that our site uses a secured https connection.
echo -n "Generating SSL certificate ...";
certbot certonly --non-interactive --agree-tos -m admin@YOUR_COMPANY.com --webroot -w $HOME/$WEBDIR -d $DOMAIN.YOUR_COMPANY.dev
echo -n "Uncommenting SSL certs in /etc/apache2/sites-available/"$DOMAIN".YOUR_COMPANY.dev.conf and run service apache2 restart"
sed -i 's/#\(.*SSL.*\)/\1/' /etc/apache2/sites-available/$DOMAIN.YOUR_COMPANY.dev.conf;
SSH account
We’re going to create an SSH account to have the ability to use the terminal to connect to a newly created website. The password is generated using the /dev/urandom function and will be displayed when the script finishes execution.
# passwd for unix username, save password
SSHPASS=`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 10 ; echo ''`
echo -e "$SSHPASS\n$SSHPASS" | passwd $DOMAIN
Git init
At this point we have a working test page with https, but without files. Let’s start by adding GIT initialization. We will fetch files from the Gitlab repository (for example files from /wp-content/themes/ and /wp-content/plugins/). Git will not ask for a password because deploy keys are already configured and the process will be done without user interaction.
# git init
sudo -u $USERNAME git init
sudo -u $USERNAME git remote add origin [email protected]_COMPANY.com:$PROJECT_NAME.git
sudo -u $USERNAME git fetch
sudo -u $USERNAME git checkout -t origin/master
sudo -u $USERNAME git reset --hard HEAD
sudo -u $USERNAME git pull
WP CLI
To add WordPress core files and create wp-config.php, we use WP CLI, a command-line interface for WordPress. It allows to fetch the newest WordPress files from the original WordPress server. At the end, we use the touch command to create WP .htaccess with default content.
printf "\Ininitializing WP Core files..\n"
# go to proper directory
cd $HOME
sudo -u $USERNAME wp core download
sudo -u $USERNAME wp config create --dbname=${DOMAIN}_db --dbuser=$DOMAIN --dbpass=$PASSWORD
sudo -u $USERNAME wp core install --url=${DOMAIN}.YOUR_COMPANY.dev --title=Website --admin_user=YOUR_WP_LOGIN --admin_password=YOUR_WP_PASSWORD --admin_email=YOUR_WP_MAIL
sudo -u $USERNAME wp option update permalink_structure '/%postname%/'
# create htaccess
sudo -u $USERNAME touch $HOME/.htaccess
sudo -u $USERNAME echo -e "$WPHTACCESS_TEMPLATE" >> $HOME/.htaccess
Htpassword
The test page will be protected by an htpasswd file. To access the website, you need to fill in the login details: testpage_user and testpage_password.
# setup htpasswd
sudo -u $USERNAME touch $HOME/.htpasswd
sudo -u $USERNAME printf "testpage_user:$(openssl passwd -crypt testpage_password)\n" >> .htpasswd
Displaying summary
One last step, our bash script will display all the details about the just created test page. We can copy and save them somewhere in the Notes for future use.
****************
( 1) test-page..................... https://test6.YOUR_COMPANY.dev
( 2) htpasswd-user................. testpage_user
( 3) htpasswd-pass................. testpage_password
( 4) git-repo...................... https://gitlab.YOUR_COMPANY.com/myrepo/wp-abc
( 5) scp/ssh-connection............ ssh test6@YOUR_COMPANY.dev
( 6) ssh-pass...................... a525fa23aab
( 7) wp-admin-user................. YOUR_WP_LOGIN
( 8) wp-admin-pass................. YOUR_WP_PASSWORD
****************
Bash script to set up the WordPress test page
The bash script starts with wizard questions, then it will start the setup. Everything takes 45-60 seconds. At the end, the test page is ready to use. To change theme files on the test page, we can add changes to the GITlab repository and then connect via SSH and fetch updates from GIT repo using the following command:
git pull
Here is a full setup-new-testpage.sh bash script file.
#!/bin/bash
# setup-new-testpage.sh
HOME_TEMPLATE="/var/www/";
FPM_TEMPLATE="/root/new-testpage/fpm.conf.template";
VHOST_TEMPLATE="/root/new-testpage/apache.virtualhost.dev.template";
rawurlencode() {
local string="${1}"
local strlen=${#string}
local encoded=""
local pos c o
for (( pos=0 ; pos<strlen ; pos++ )); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9] ) o="${c}" ;;
* ) printf -v o '%%%02x' "'$c"
esac
encoded+="${o}"
done
echo "${encoded}" # You can either set a return variable (FASTER)
#REPLY="${encoded}" #+or echo the result (EASIER)... or both... :p
}
WPHTACCESS_TEMPLATE=$(cat <<-END
# BEGIN WordPress
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
END
)
### action!
if [ -z $1 ]
then
echo -n "Please provide subdomain name without www (i.e. PROJECT for PROJECT.YOUR_COMPANY.dev) and press [ENTER]: ";
read DOMAIN;
else
DOMAIN=$1;
echo -n "Domain: "$DOMAIN".YOUR_COMPANY.dev";
fi
if [ -z "$2" ]
then
echo -n "Please provide username and press [ENTER]: ";
read USERNAME;
else
USERNAME=$2;
echo -n "Username: "$USERNAME;
fi
if [ -z "$3" ]
then
echo -n "Please provide webdir and press [ENTER] (WP leave empty, SF enter: public)":
read WEBDIR;
else
if [ $3 != "wp" ]
then
WEBDIR=$3;
echo -n "Selected webdir - "$WEBDIR" for site";
else
WEBDIR='';
echo -n "Wordpress webdir";
fi
fi
#USERNAME=$DOMAIN
if [ -z "$4" ]
then
echo -n "Please provide which version of PHP to use (type php7 or php5 and hit [ENTER]): ";
read PHPVER;
if [ $PHPVER != "php5" -a $PHPVER != "php7" ]
then
echo -n "Wrong version of PHP selected. Please type php5 or php7";
exit;
fi;
else
if [ $4 != "php5" -a $4 != "php7" ]
then
echo -n "Wrong version of PHP";
exit;
else
PHPVER=$4
echo -n "Selected PHP - "$PHPVER" - is correctly";
fi
fi
if [ -z "$5" ]
then
echo -n "Please provide gitlab project name, ex. YOUR_COMPANY/project -> ";
read PROJECT_NAME;
else
PROJECT_NAME=$5
echo -n "Provided gitlab project name: "$PROJECT_NAME;
fi
# setup new wordpress
if [ -z "$6" ]
then
echo -n "Initialize WordPress [ENTER] (WP y, No n)":
read ISWP;
else
ISWP='n';
fi
HOME=$HOME_TEMPLATE$DOMAIN.YOUR_COMPANY.dev
echo -n "Creating home directory $HOME... ";
mkdir -p $HOME;
echo "done";
echo -n "Creating $USERNAME user... ";
adduser --home $HOME --shell /bin/bash --ingroup www-users --disabled-password --quiet --gecos "" --no-create-home $USERNAME;
echo "done";
echo -n "Chowning home directory... ";
chown $USERNAME.www-users $HOME;
echo "done";
echo -n "Chmodding home directory... ";
chmod 701 $HOME;
echo "done";
echo -n "Preparing SSH keys for user $USERNAME... ";
sudo -u $USERNAME ssh-keygen -b 2048 -t rsa -f $HOME/.ssh/id_rsa -q -N ""
cat $HOME/.ssh/id_rsa.pub >> $HOME/.ssh/authorized_keys
chown $USERNAME.www-users $HOME/.ssh/authorized_keys
chmod 600 $HOME/.ssh/authorized_keys;
echo "done";
echo -n "Preparing virtualhost file... ";
sed -e 's/{{DOMAIN}}/'$DOMAIN'/g' -e 's/{{PHPVER}}/'$PHPVER'/g' -e 's/{{WEBDIR}}/'$WEBDIR'/g' $VHOST_TEMPLATE > /etc/apache2/sites-available/$DOMAIN.YOUR_COMPANY.dev.conf
echo "done";
echo -n "Preparing apache logs directory...";
mkdir -p /var/log/apache2/$DOMAIN;
echo "done";
echo -n "Preparing php-fpm file... ";
sed -e 's/{{DOMAIN}}/'$DOMAIN'/' -e 's/{{USERNAME}}/'$USERNAME'/' -e 's/{{PHPVER}}/'$PHPVER'/g' < $FPM_TEMPLATE > /etc/$PHPVER/fpm/pool.d/$DOMAIN.YOUR_COMPANY.dev.conf
echo "done";
#echo -n "Enabling virtualhost... ";
a2ensite -q $DOMAIN.YOUR_COMPANY.dev;
#echo "done";
mkdir $HOME/$WEBDIR
echo -n "Restarting php5-fpm... ";
service php5-fpm restart
echo "done";
echo -n "Restarting php7-fpm... ";
#service php7.0-fpm restart
#service php7.1-fpm restart
service php7.3-fpm restart
echo "done";
echo -n "Restarting apache ...";
service apache2 restart
echo "done";
echo -n "Creating database for user "$USERNAME"...";
MYSQL=`which mysql`;
PASSWORD=`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 10 ; echo ''`
Q1="CREATE DATABASE IF NOT EXISTS "$DOMAIN"_db;"
Q2="GRANT ALL ON *.* TO '$USERNAME'@'localhost' IDENTIFIED BY '$PASSWORD';"
Q3="FLUSH PRIVILEGES;"
SQL="${Q1}${Q2}${Q3}"
$MYSQL -uroot -pYOUR_MYSQL_PASSWORD -e "$SQL"
echo -n "Database "$DOMAIN"_db created";
echo -n "Now, we've deploy keys to gitlab.";
PUBLIC_KEY=`cat $HOME/.ssh/id_rsa.pub`;
ssh-keyscan gitlab.YOUR_COMPANY.com >> $HOME/.ssh/known_hosts
PROJECT_ESCAPED=`rawurlencode $PROJECT_NAME`;
echo -n "Adding deploy keys";
curl --request POST --header "PRIVATE-TOKEN: YOUR_PRIVATE_TOKEN" --header "Content-Type: application/json" --data '{"title": "'$DOMAIN' dev.YOUR_COMPANY.com", "key": "'"$PUBLIC_KEY"'", "can_push": "false"}' https://gitlab.YOUR_COMPANY.com/api/v4/projects/$PROJECT_ESCAPED/deploy_keys;
echo -n "Public keys deployed";
echo -n "Generating SSL certificate ...";
certbot certonly --non-interactive --agree-tos -m admin@YOUR_COMPANY.com --webroot -w $HOME/$WEBDIR -d $DOMAIN.YOUR_COMPANY.dev
echo -n "Uncommenting SSL certs in /etc/apache2/sites-available/"$DOMAIN".YOUR_COMPANY.dev.conf and run service apache2 restart"
sed -i 's/#\(.*SSL.*\)/\1/' /etc/apache2/sites-available/$DOMAIN.YOUR_COMPANY.dev.conf;
echo -n "Done";
# GIT INIT
# go to proper directory
cd $HOME
# setup htpasswd
sudo -u $USERNAME touch $HOME/.htpasswd
sudo -u $USERNAME printf "testpage_user:$(openssl passwd -crypt testpage_password)\n" >> .htpasswd
# git init
sudo -u $USERNAME git init
sudo -u $USERNAME git remote add origin [email protected]_COMPANY.com:$PROJECT_NAME.git
sudo -u $USERNAME git fetch
sudo -u $USERNAME git checkout -t origin/master
sudo -u $USERNAME git reset --hard HEAD
sudo -u $USERNAME git pull
# passwd for unix username, save password
SSHPASS=`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 10 ; echo ''`
echo -e "$SSHPASS\n$SSHPASS" | passwd $DOMAIN
# setup new wordpress
if [ $ISWP != "y" ]
then
# do nothing
echo -n "do nothing"
else
printf "\Ininitializing WP Core files..\n"
# go to proper directory
cd $HOME
sudo -u $USERNAME wp core download
sudo -u $USERNAME wp config create --dbname=${DOMAIN}_db --dbuser=$DOMAIN --dbpass=$PASSWORD
sudo -u $USERNAME wp core install --url=${DOMAIN}.YOUR_COMPANY.dev --title=Website --admin_user=YOUR_WP_LOGIN --admin_password=YOUR_WP_PASSWORD --admin_email=YOUR_WP_MAIL
sudo -u $USERNAME wp option update permalink_structure '/%postname%/'
# create htaccess
sudo -u $USERNAME touch $HOME/.htaccess
sudo -u $USERNAME echo -e "$WPHTACCESS_TEMPLATE" >> $HOME/.htaccess
fi
# Display all details:
printf "\n****************\n"
list=(test-page htpasswd-user htpasswd-pass git-repo scp/ssh-connection ssh-pass wp-admin-user wp-admin-pass)
list2=("https://$DOMAIN.YOUR_COMPANY.dev" $DOMAIN $DOMAIN https://gitlab.YOUR_COMPANY.COM/$PROJECT_NAME "ssh [email protected]_COMPANY.com" $SSHPASS "testpage_user" "testpage_password")
C=1
for M in "${list[@]}"
do
machine_indented=$(printf '%-30s' "$M")
machine_indented=${machine_indented// /.}
printf "(%2d) %s ${list2[$C-1]}\n" "$C" "$machine_indented"
((C=C+1))
done
printf "\n****************\n"
service apache2 restart
echo "APACHE 2 restarted";
echo -n "All done!";
FPM config
PHP-FPM (FastCGI Process Manager) is a server tool to speed up the performance of the site. It’s faster than traditional CGI and can handle heavy load simultaneously. Here is the location of our FPM template: /root/new-testpage/fpm.conf.template
[{{DOMAIN}}]
user = {{USERNAME}}
group = www-users
listen = /var/run/{{PHPVER}}-fpm-{{DOMAIN}}.sock
listen.owner = users
listen.group = users
pm = dynamic
pm.max_children = 30
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /
Apache virtualhost
The term Virtual Host (vhost) refers to the practice of running more than one website (such as company1.example.com and company2.example.com) on one server. We’re using the “name-based” vhost, meaning that multiple names are running on one IP address. We’re using a predefined template. Here is the source code of /root/new-testpage/apache.virtualhost.dev.template :
<VirtualHost *:80>
ServerName {{DOMAIN}}.YOUR_COMPANY.dev
DocumentRoot "/var/www/{{DOMAIN}}.YOUR_COMPANY.dev/{{WEBDIR}}"
#allow access for Lets Encrypt
RewriteEngine on
RewriteCond "/var/www/{{DOMAIN}}.YOUR_COMPANY.dev/{{WEBDIR}}%{REQUEST_URI}" !-f
RewriteRule ^(.*)$ https://{{DOMAIN}}.YOUR_COMPANY.dev/ [last,redirect=301]
</VirtualHost>
<VirtualHost *:443>
ServerName {{DOMAIN}}.YOUR_COMPANY.dev
ServerAlias *.{{DOMAIN}}.YOUR_COMPANY.dev
DocumentRoot "/var/www/{{DOMAIN}}.YOUR_COMPANY.dev/{{WEBDIR}}"
DirectoryIndex index.php index.html
Options -Indexes
<Directory "/var/www/{{DOMAIN}}.YOUR_COMPANY.dev/">
AllowOverride All
Allow from All
AuthType Basic
AuthName "Test-page area"
AuthUserFile /var/www/{{DOMAIN}}.YOUR_COMPANY.dev/.htpasswd
Require valid-user
</Directory>
<IfModule mod_php5.c>
php_admin_flag engine off
</IfModule>
<FilesMatch \.php$>
SetHandler None
</FilesMatch>
<IfModule mod_fastcgi.c>
AddType application/x-httpd-fast{{PHPVER}} .php
Action application/x-httpd-fast{{PHPVER}} /{{PHPVER}}-fcgi-{{PHPVER}}-fpm-{{DOMAIN}}
Alias /{{PHPVER}}-fcgi-{{PHPVER}}-fpm-{{DOMAIN}} /usr/lib/cgi-bin/{{PHPVER}}-fcgi-{{PHPVER}}-fpm-{{DOMAIN}}
FastCgiExternalServer /usr/lib/cgi-bin/{{PHPVER}}-fcgi-{{PHPVER}}-fpm-{{DOMAIN}} -socket /var/run/{{PHPVER}}-fpm-{{DOMAIN}}.sock -pass-header Authorization
</IfModule>
#SSLEngine on
#SSLCertificateKeyFile /etc/letsencrypt/live/{{DOMAIN}}.YOUR_COMPANY.dev/privkey.pem
#SSLCertificateChainFile /etc/letsencrypt/live/{{DOMAIN}}.YOUR_COMPANY.dev/fullchain.pem
#SSLCertificateFile /etc/letsencrypt/live/{{DOMAIN}}.YOUR_COMPANY.dev/cert.pem
ErrorLog /var/log/apache2/{{DOMAIN}}/error.log
CustomLog /var/log/apache2/{{DOMAIN}}/access_log common
</VirtualHost>
Creating a new test site
To use this script, just change 3 strings:
YOUR_COMPANY, YOUR_MYSQL_PASSWORD and YOUR_PRIVATE_TOKEN
To change WordPress access to wp-admin panel, those values should be replaced in bash script:
YOUR_WP_LOGIN, YOUR_WP_PASSWORD and YOUR_WP_MAIL.
The website will be set up at: https://subdomain.YOUR_COMPANY.dev url address.
To execute the bash script, we need to add permissions for execution, it’s a one-time operation:
chmod u+x setup-new-testpage.sh
Now, we can run the wizard for a new test page setup:
./setup-new-testpage.sh
Bash wizard
After script execution, we will be asked 6 questions.
root $ ./setup-new-testpage.sh
Please provide subdomain name without www (i.e. PROJECT for PROJECT.YOUR_COMPANY.dev) and press [ENTER]: test5
Please provide username and press [ENTER]: test5
Please provide webdir and press [ENTER] (WP leave empty, SF enter: public):
Please provide which version of PHP to use (type php7 or php5 and hit [ENTER]): php7
Please provide gitlab project name, ex. YOUR_COMPANY/project -> maciek/wordpress-http2
Initialize WordPress [ENTER] (WP y, No n):y
All tasks for staging site setup take 45-60 seconds. After finishing all of its “magic”, the script will display a success message:
Downloading WordPress 5.9.1 (en_US)...
md5 hash verified: 5bbe205b48cf9255fd7c954040aeb125
Success: WordPress downloaded.
Success: Generated 'wp-config.php' file.
Success: WordPress installed successfully.
Success: Updated 'permalink_structure' option.
****************
APACHE 2 restarted
All done!
Alternatives
Not feeling strong in server configuration and running your own test pages? There are alternatives. The most popular one is using hosting companies. New pages can be created by clicking a button in the hosting panel. Some of them offer GIT repositories and an easy way of cloning a website to the staging environment – WP ENGINE being one of them.
There are tradeoffs of using Hosting companies. It’s more pricey than running your own server. However, it can be convenient for someone who is not a server expert. It’s worth to mention that hosting companies also offer additional services like WAF Firewalls and regular server updates, which will help with the security of your website.
That’s it for today’s tutorial. Make sure to follow us for other useful tips and guidelines, and don’t forget to subscribe to our newsletter.