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.

e<span style=“background-color: initial; font-family: inherit; font-size: inherit; color: initial;”>cho -n “Creating database for user “$USERNAME“…”;</span>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 git@gitlab.YOUR_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 git@gitlab.YOUR_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.

Was this article helpful?

Related Articles

Leave A Comment?