How to Setup Multiple WordPress Sites on WebDock VPS

WordPress is on of the most used CMS (content management system) that allows user to create blogs, e-commerce sites, portfolio, news site, headless servers and many more. WordPress needs a web server (Apache, Nginx, LiteSpeed), database (MariaDB, MySQL) and PHP.

In this tutorial we are going to deploy multiple WordPress sites on WebDock. WebDock is a fast and cheap VPS provider that fits everyone’s need and provides semi managed and ummanged VPS servers. WebDock provides their own control panel where you can manage your VPS servers like, installing LEMP stack, WordPress, phpMyAdmin, MonogDB, CertBot, FTP etc. Hence I described it as semi managed. But the problem is their control panel does not support multiple WordPress installations and they suggest to have single site per server. This is the reason this tutorial about installing multiple WordPress with LEMP in a single VPS server exists.

Create a Server

Create a new server and select Clean OS instead of The Perfect Server. Since WebDock does not support multiple sites in a single server we should manage our server ourselves to avoid accidental changes from WebDock control panel. And this is they way I recommend if you want to have multiple sites or web apps in your VPS. A web server can run at 512 MB RAM too but I recommend to have at least 2 GB of RAM for multiple WordPress sites.

choose clean os

WebDock only supports Ubuntu for now hence we will get Ubuntu Focal 20.4 as our server. Give a name and slug and spin up your server.

server created

Congratulations you have your server running and ready to deploy our site.

Setup SSH

At bottom of the dashboard click on add shell user then provide username and password and press ADD USER. You don’t need to change other options and you should not if you don’t know what are you doing.

add user

After adding user to your server you need to login to the server in order to make changes and to do so you need to add SSH key first. SSH is generated in you local machine and a copy of public key is uploaded to server so that server knows who is trying to connect.

Generate SSH Key

To create SSH key, in your local machine in terminal use ssh-keygen.

~$ ssh-keygen
 Generating public/private rsa key pair.
 Enter file in which to save the key (/home/user/.ssh/id_rsa):

If you already have SSH key then a overwrite message will be shown and you don’t need to create a new key. Then it will ask for passphrase. Adding passphrase is recommended.

~$ ssh-keygen
 Generating public/private rsa key pair.
 Enter file in which to save the key (/home/user/.ssh/id_rsa): 
 Enter passphrase (empty for no passphrase): 
 Enter same passphrase again: 
 Your identification has been saved in /home/user/.ssh/id_rsa
 Your public key has been saved in /home/user/.ssh/id_rsa.pub
 The key fingerprint is:
 SHA256:6H5M8HkmTD1BPkPCA67a8CdWR7KKx8b4kN6fTMndQhQ
 user@myPc
 The key's randomart image is:
 +---[RSA 3072]----+
 |      .oE.o      |
 |     .  o=.      |
 |      o oo+.     |
 |     ..<em>. oo     | |  . . +=S. .     | |   @ = =</em>.o      |
 |  * @ *oo+.      |
 | . B * .o.       |
 |  . o.=.         |
 +----[SHA256]-----+

This is what you see after completing generating SSH key.

Copy Public Key

As our key is saved in ~/.ssh/id_rsa.pub we can get public key using cat.

~$ cat ~/.ssh/id_rsa.pub

It will give result like this;

~$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7DFwx4hgyBKvhqd5ss56YIhU2PMgO1NEQ4Kln8dnHit6eLF4HKzCPfsIhkeWUGu4VdqS4Zg+U04Xvx/TG1z3N9klnvV7pHeZaxGciUT8+Ii6YUWosBUX/Ki8ZfC+QgmzkGmQwIefXzxOMijqa1j7CJH8JFigQYCw9B6LIwNhageQjPyPy68ido/XDuqI1xtbr/aJcuYxE2IHVzsNzMAoJlNEVjrhwTp0y78bWW2RodftPPNT670MwrFfiP+iqck1zQ/n+VwB9wW67cFlE/HX6Invc13i/psjn6UJcpKEDW3tHvvpX6Y/Nik79dSSTAX9lAAuOKwe3VJ6YQBSPpE0JInVE0veO+xEJkTnXz+kUWgQQT+PxfHSIE63v0mIAiPTW3WnFkZkjW23BZX+2Tty6xPyHuXJGtaiyyaZ+F4/A51jZ3yXR2G7pLOy9PdeDl1j3Doy466UX86p0zpPEG5KpRuis/JMve5vvPQBwppoBHCQTdkb2ApvA2XL3vtxoMyk= user@myPc

Copy all the output content and save to WebDock.

Then assign key to the user.

Login to Server using SSH

We can get server IP address from the servers list.

server ip

To login into server using SSH;

~$ ssh admin@45.148.30.37

Install LEMP Stack

L: We already installed Linux.

Install Nginx (pronounce as Engine-X)

admin@webdocktutor:~$ sudo apt update
admin@webdocktutor:~$ sudo apt install nginx

Browse to your IP http://your_ip_address, for me it is http://45.148.30.37. We can see nginx Welcome page. Which means nginx is installed correctly.

welcome to nginx

Install MariaDB

MariaDB or MySQL can be installed for database used by WordPress.

admin@webdocktutor:~$ sudo apt install mariadb-server

After installing we need to setup MariaDB for the first time.

admin@webdocktutor:~$ sudo mysql_secure_installation

Create new root password and select yes for other options.

Create new user and database for WordPress site. Login into mariadb.

admin@webdocktutor:~$ sudo mariadb

Now create wordpress1 user and wordpress2 user for 2 WordPress installation. If you don’t want to install 2 WordPress sites you don’t need to create wordpress2 user. You can have your own names and don’t need to have exact wordpress name. Don’t forget ; after sql command. If you forgot ; and have pressed enter, MySQL shell will go to new line. You can enter forgotten ; and hit enter again to continue.

MariaDB [(none)]> CREATE DATABASE wordpress1;
MariaDB [(none)]> CREATE DATABASE wordpress2; 

To view newly created database you can use SHOW DATABASES command.

MariaDB [(none)]> SHOW DATABASES;

Create new users for wordpress1 and wordpress2 databases.

MariaDB [(none)]> GRANT ALL ON wordpress1.* TO 'wordpress1user'@'localhost' IDENTIFIED BY 'password1' WITH GRANT OPTION;
MariaDB [(none)]> GRANT ALL ON wordpress2.* TO 'wordpress2user'@'localhost' IDENTIFIED BY 'password2' WITH GRANT OPTION;

We created users wordpress1user and wordpress2user and gave them full privileges to their respective databases wordpress1 and wordpress2. You can always have users and database as your own choice. For example, you can give wordpress1user access to both database without need to create wordpress2user. But for security we created users for each databases.

Keep these root password, usernames and passwords at safe place. We need it later and also we may need in future.

Flush the privileges to ensure that they are available in the current session.

MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> exit;

Login again to MariaDB and see databases if they are available or not. They should appear.

admin@webdocktutor:~$ sudo mariadb
MariaDB [(none)]> SHOW DATABASES;
MariaDB [(none)]> show databases;
 +--------------------+
 | Database           |
 +--------------------+
 | information_schema |
 | mysql              |
 | performance_schema |
 | wordpress1         |
 | wordpress2         |
 +--------------------+
 5 rows in set (0.001 sec)

It is not necessary to write sql command in all capital letters. To exit;

MariaDB [(none)]> exit;

Install PHP

Nginx uses php-fpm to process PHP. Install php-fpm and php-mysql:

admin@webdocktutor:~$ sudo apt install php-fpm php-mysql

So LEMP stack is installed. Now we need to setup nginx to run php. By default it is does not run php.

Configure NGINX to use PHP-FPM

In nginx config file there are server blocks that defines web servers. These server blocks need domain name to work. WebDock gives a subdomain for all servers and in my case it is webdocktutor.vps.webdock.io. You can find yours in sever overview page named as alias.

You can use this domain name to test your nginx configuration.

All web files are saved inside /var/www/ and here is html folder inside it which we already seen by browsing our IP address http://ip-address.

In this tutorial I am assuming that we have domain names, mycoolsite.com and mycoolblog.com. So all configuration from now will be based on these 2 domain names.

For our 2 WordPress sites, create 2 folders with the same name as our domain names, which is, mycoolsite.com and mycoolblog.com. So that our new directories are /var/www/mycoolsite.com/and /var/www/mycoolblog.com/. Inside those directories we will place WordPress files and setup nginx config to server them.

Configure NGINX config file for multiple sites

Nginx saves all config in /etc/nginx/nginx.conf but as we are in Ubuntu server config files are available in separate folder to match Apache config directories. Generally all available sites/servers config files are kept inside /etc/nginx/sites-available/ and to enable those sites symbolic link to available sites must be added to /etc/nginx/sites-enabled/.

If you take a look at /etc/nginx/sites-available/ you will find a file named default which is responsible to serve files under /var/www/html/. So what we need to do to setup our WordPress sites? Well, we will remove the symbolic link of default from /etc/nginx/sites-enabled/ and create our own config file then create a link.

To remove default site use:

admin@webdocktutor:~$ sudo rm /etc/nginx/sites-enabled/default

This will remove symbolic link but not the actual config file under /etc/nginx/sites-available/ and that is how is should be done.

Now create a new config file to make WordPress (PHP) working:

admin@webdocktutor:~$ sudo nano /etc/nginx/snippets/wordpress.conf

And put this inside wordpress.conf:

# /etc/nginx/snippets/wordpress.conf
# this should be used inside server block

index index.php;

location / {
        # try to serve static file if not found then directory then php
        # include the "?$args" part so non-default permalinks doesn't break when usi>
        try_files $uri $uri/ /index.php?$args;
}

# php-fpm
location ~ \.php$ {
        include fastcgi_params;
        # you may need to change this fastcgi_pass to match your php version
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_intercept_errors on;
        # The following parameter can be also included in fastcgi_params file
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

# favicon.ico
location = /favicon.ico {
        log_not_found off;
        access_log off;
}

# robots.txt related
location = /robots.txt {
        # wordpress dynamically generates robots.txt
        try_files $uri $uri/ /index.php?$args;

        allow all;

        log_not_found off;
        access_log off;
}

# sitemap related
location ~* \.(xml|xsl)$ {
        # wordpress dynamically generates sitemap.xml
        try_files $uri $uri/ /index.php?$args;

        log_not_found off;
        access_log off;
}

# static assets caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;
}


# RESTRICTIONS
# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~ /\. {
        deny all;
}


# Deny access to any files with a .php extension in the uploads directory
# Works in sub-directory installs and also in multisite network
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~* /(?:uploads|files)/.*\.php$ {
        deny all;
}

You may need to have a look in location ~ \.php$ {} block since it may be different in your setup. Because it is for php7.4.

Now we need to create virtual host and use above config. To create virtual hosts for our 2 different domain names create config files inside sites-available folder.

admin@webdocktutor:~$ sudo nano /etc/nginx/sites-available/mycoolsite.com

Then pust this config:

server {        
        root /var/www/mycoolsite.com;

        server_name mycoolsite.com;

        listen 80;
        listen [::]:80;
   
        # WordPress core
        include snippets/wordpress/wordpress.conf;
}

Similarly for another domain name:

admin@webdocktutor:~$ sudo nano /etc/nginx/sites-available/mycoolblog.com
server {        
        root /var/www/mycoolblog.com;

        server_name mycoolblog.com;

        listen 80;
        listen [::]:80;
   
        # WordPress core
        include snippets/wordpress/wordpress.conf;
}

So now when you browse these domains it will try to server the content from directory specified as root. If you browse you domains now you will get 404 not found error since we haven’t placed anything in those directories.

Now it’s time to install WordPress. Navigate to /tmp directory and download WordPress.

cd /tmp
wget https://wordpress.org/latest.tar.gz

Extract the files:

tar -zxvf latest.tar.gz

Change files permission to www-data user/group so that we won’t get any permission error when using WordPresss.

sudo chown -R www-data:www-data wordpress

Now opy files to /var/www/mycoolsite.com and /var/www/mycoolblog.com

sudo cp -a wordpress/. /var/www/mycoolsite.com/
sudo cp -a wordpress/. /var/www/mycoolblog.com/

Now create a symlink from sites-available to sites-enabled.

sudo ln -s /etc/nginx/sites-available/mycoolblog.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/mycoolsite.com /etc/nginx/sites-enabled/

Check nginx configuration:

sudo nginx -t

If everything is ok reload nginx:

sudo nginx -s reload

Install Let’s Encrypt SSL

Make sure you have already installed certbot, if not then install from here – https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal. From the dropdown you can select different OS than Ubuntu. We are going to manually config our SSL so only get the certificates we needed. certbot will prompt domain names select them and continue.

sudo certbot certonly --nginx

After successfully getting certificate from cerbot command it will provide you certificates /etc/letsencrypt/live/mycoolsite.com/fullchain.pem and /etc/letsencrypt/live/mycoolsite.com/privkey.pem . After than we need to use those with Nginx. Open the config file we previously created for each sites, e.g. /etc/nginx/sites-available/mycoolsite.com. And config SSL like this.

server {        
        root /var/www/mycoolsite.com;

        server_name mycoolsite.com;

        listen 80;
        listen [::]:80;
   
        # WordPress core
        include snippets/wordpress/wordpress.conf;
     
        # SSL
        ssl_certificate /etc/letsencrypt/live/mycoolsite.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/mycoolsite.com/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

Now visit those domains/urls, mycoolblog.com and mycoolsite.com you will find WordPress installation screen. Provide necessary info, database name, username, password we have created previously.

I hope you find this aricle helpful.

This Post Has 2 Comments

  1. Amber

    Can you please update this post

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.