CreateIT
CreateIT
BLOG

Set up a WordPress Test Site in 60 seconds using Bash Script

Black ninja presenting a monitor screen with icons

Set up a WordPress Test Site in 60 seconds using Bash Script

SHARE

Challenge:
automate configuration of a new WP site
Solution:
prepare a bash script that will configure an Apache site and use WP CLI to set up WordPress

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 [email protected]_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";

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 [email protected]_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 [email protected]_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!

black box with code

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.

Need help?

  • Looking for support from experienced programmers?

  • Need to fix a bug in the code?

  • Want to customize your webste/application?

ADD COMMENT

Your email address will not be published.

createIT Contact