Set up a WordPress test site in 60 seconds using Bash Script
Get a free advice now!

    Pick the topic
    Developer OutsourcingWeb developingApp developingDigital MarketingeCommerce systemseEntertainment systems

    Thank you for your message. It has been sent.

    Set up a WordPress test site in 60 seconds using Bash Script

    August 30, 2022
    Last update: September 4, 2024
    8 min read
    50
    0
    0
    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.

    Support – Tips and Tricks
    All tips in one place, and the database keeps growing. Stay up to date and optimize your work!

    Contact us