Hosting WordPress Yourself Part 1 – Setting Up a Secure Virtual Server

Welcome to the first post in the series on Hosting WordPress Yourself, in which I will guide you through the process of building a complete server to house your WordPress sites. I’m going to start at the very beginning and detail the complete approach I personally take to build a new server that’s easy to configure, secure, tuned for optimal WordPress performance and scalable for small to medium sized sites.

The server is going to be designed for single user access. Therefore this setup isn’t going to be suitable for a shared hosting environment, but is ideal for personal and fully managed client sites. Depending on the virtual server package you choose, this setup should comfortably handle multiple sites on a single server.

In this tutorial series I’m going to use a Digital Ocean 512MB droplet ($5/month), but you could also choose Linode, Amazon EC2, or one of the many other virtual server providers. Regardless of which you choose, the steps following the initial server creation should be very similar. The steps detailed in this series are written for Mac users, but Windows users shouldn’t find too many differences once you get past part 1. I’m also assuming you are comfortable using the command line. Finally, although designed for WordPress, this setup should work equally well for other PHP open source systems.

So, without further ado, let’s begin building your new server!

Selecting a Linux Distribution

I’m not going to go into detail on the initial droplet (server) creation as Digital Ocean has their own tutorial, but I do want to list the reasons I choose Ubuntu as my Linux distribution and how I pick a release version. Ubuntu is one of the most popular distributions for servers and here’s a few reasons why:

  1. Heavily focussed on usability
  2. A large selection of packages
  3. Frequent software updates
  4. A large community leading to more helpful resources

One drawback to choosing a distribution with frequent software updates is that it’s possible to introduce new bugs and conflicts. Luckily, Ubuntu has a LTS (Long Term Support) release, which uses packages that are considered more stable. LTS releases occur every 2 years and are supported for 5 years – making them suitable for production servers. At the time of writing, the latest LTS release is 16.04, which is the version I’m going to choose:

With the distribution and release version selected, go ahead and create a new droplet. Remember to select a region that is closest to the majority of your base audience.

First Login

Once the server has finished provisioning you should receive an email containing the droplet IP address, username and password. Begin by connecting to your new droplet and changing the root password, which is required on first login:

ssh root@178.62.***.***

Although you are going to disable root login a bit later, you should set a strong password (using a tool like 1Password), as you will still be able to switch to the root user once logged in.

Setting the Hostname

Now that you’re logged into the server, let’s set the hostname and fully qualified domain name (FQDN). The hostname should be unique but doesn’t require any relationship to the sites that will be hosted, for example, people often name their servers after astronomical objects. Correctly setting the hostname and FQDN will make connecting to your server much easier in the future as you won’t have to remember the IP address each time. To set the hostname, issue the following commands (altered for your chosen domain name):

echo "pluto.ashleyrich.com" > /etc/hostname
hostname -F /etc/hostname

In order to connect to the server using your hostname you need to update your domain name’s DNS settings. Log into your DNS control panel and create a new A record:

Hostname - Image DNS

Now if you exit out of the current SSH session you should be able to connect to the server using the new hostname. However, you may need to wait a while for the DNS settings to propagate:

ssh root@pluto.ashleyrich.com

Setting the Timezone

So that the server is running on your local timezone you must configure the tzdata package, which will ensure that the system log files show the correct date and time. The following command will allow you to configure the tzdata package:

dpkg-reconfigure tzdata

A simple GUI will be displayed, allowing you to select your geographic area and time zone:

Once completed, the newly selected timezone will be displayed along with the current time and date:

Installing Software Updates

Although you have only just provisioned your new server, it is likely that some software packages are out of date. Let’s ensure you are using the latest software by pulling in updated package lists:

apt-get update

Once completed, let’s update all of the currently installed packages. You will be prompted with how much space the updates will take – hitting Y and Enter will begin the process:

apt-get upgrade

When the upgrades have completed you will be shown which packages have installed, and also which packages are no longer required by the system.

Feel free to remove the outdated packages by issuing the following command:

apt-get autoremove

Creating a New User

It’s time to add a new user to your server. There are two reasons for this:

  1. Later in this tutorial you are going to disable root login, which means you need another user account in order to access your server.
  2. The root user contains very broad privileges which will allow you to execute potentially destructive commands. Therefore it’s advised to create a new user account with more limited permissions for day-to-day use. This new user will be added to the sudo group so that you can execute commands which require heightened permissions, but only when required.

First, create the new user:

adduser ashley

You’ll be prompted to enter some basic user information and to select a password. As mentioned previously, this password should be complex:

Next you need to add the new user to the sudo group:

usermod -a -G sudo ashley

Now ensure your new account is working by logging out of your current SSH session and initiating a new one:

logout

Then login with the new account:

ssh ashley@pluto.ashleyrich.com

Generating a Key Pair

At this point your new user is ready to use, however for enhanced security you are going to add public key authentication. I’m not going to go into detail on how to create an SSH key pair (Digital Ocean have an informative article on the process), but if you don’t already have one, enter the following command on your local machine:

ssh-keygen

You should receive a message like I have below, just hit return to accept the default location. You’ll then be prompted to enter a passphrase (optional), which will require you to enter a password every time you login with this key pair:

Copying the Public Key

Now that you have your SSH key pair, you need to copy the public key to your server. Assuming you saved the key in the default location, the following command will print the key to your console window:

cat ~/.ssh/id_rsa.pub

Select the outputted string and copy it to your clipboard. Go back to your SSH session, ensuring you are logged in with the newly created user. Now create the .ssh directory and set the correct permissions:

mkdir   ~/.ssh
chmod 700 ~/.ssh

Within the .ssh directory create a new file called authorized_keys which contains the public key you copied from your local machine:

nano ~/.ssh/authorized_keys

Save the file using CTRL-X and then Y. Finally, set the correct permissions on the file:

chmod 600 ~/.ssh/authorized_keys

Now if you logout of the current SSH session and try reconnecting, you should no longer have to enter your user password. Remember, if you set a passphrase when creating the SSH key, you will need to enter it when prompted.

SSH Configuration

With your new user created, it’s time to further secure the server by configuring SSH. The first thing you are going to do is disable root login, which as the name suggests will no longer let you log into the server via SSH using the default root user. Open the SSH configuration file using nano (notice the use of sudo to heighten privileges for this command):

sudo nano /etc/ssh/sshd_config

Find the line that reads PermitRootLogin yes and change it to PermitRootLogin no. Hit CTL-X then Y to save the changes. In order for the changes to take affect you must restart the SSH service:

sudo service ssh restart

Now if you exit out of the current SSH session and try connecting with the root user you should receive a permission denied error message.

The final step to securing SSH is to disable user login using a password. This ensures that attackers need your private SSH key to login to the server. Remember, if you lose your private key you will be locked out of the server, so keep it safe! Most virtual server providers do have other means of logging in, but it’s best not to rely on those methods:

sudo nano /etc/ssh/sshd_config

Find the line that reads #PasswordAuthentication yes and change it to PasswordAuthentication no. Hit CTL-X then Y to save the changes. Once again, you must restart the SSH service for the changes to take effect.

sudo service ssh restart

Now, before you log out of your server, you should test your new configuration. To do this open a new terminal window, without closing the current SSH session and attempt to connect:

ssh ashley@pluto.ashleyrich.com

You should login to the server successfully. To further test that password authentication is disabled I like to temporarily rename the SSH key located in my .ssh directory. When attempting to log into the server this time you should receive a Permission denied (publickey) error.

Firewall

The firewall provides an additional layer of security to your server by blocking inbound network traffic. In this article I’m going to demonstrate the iptables firewall, which is the most commonly used across Linux and is installed by default. In order to simplify the process of adding rules to the firewall I like to use a package called ufw, which stands for Uncomplicated Firewall. The ufw package is usually installed by default, but if it isn’t go ahead and install it using the following command:

sudo apt-get install ufw

Now that you have access to ufw you can begin adding to the default rules, which deny all incoming traffic and allow all outgoing traffic. For now, add the ports for SSH (22) and HTTP (80):

sudo ufw allow ssh
sudo ufw allow http

If you plan on running a site using HTTPS you will also need to add port 443:

sudo ufw allow https

To review which rules will be added to the firewall, enter the following command:

sudo ufw show added

Before enabling the firewall rules, ensure that the port for SSH is in the list of added rules – otherwise you won’t be able to connect to your server! The default port is 22. If everything looks correct, go ahead and enable the configuration:

sudo ufw enable

To confirm that the new rules are active, enter the following command:

sudo ufw status verbose

You will see that all inbound traffic is denied by default except on ports 22, 80 and 443 for both IPv4 and IPv6, which is good starting point for most servers.

Fail2ban

Fail2ban is a tool which works alongside your firewall. It functions by monitoring intrusion attempts to your server and blocks the offending host for a set period of time. It does this by adding any IP addresses that show malicious activity to your firewall rules.

The Fail2ban program isn’t installed by default, so let’s install it now:

sudo apt-get install fail2ban

The default configuration should suffice, which will ban a host for 10 minutes after 6 unsuccessful login attempts via SSH. To ensure the fail2ban service is running enter the following command:

sudo service fail2ban start

Job done! You now have a good platform to begin building your WordPress web server and have taken the necessary steps to prevent unauthorised access. However, it’s important to remember that security is an ongoing process and you should keep in mind the following points:

  1. Install only required software from trusted sources
  2. Regularly install software updates and security fixes
  3. Enforce strong passwords using a tool such as 1Password
  4. Use common sense and think about how you would gain access to the server if you were locked out

That’s all for part 1, in the next post I will guide you through installing Nginx, PHP-FPM and MariaDB. Until next time!

About the Author

Ashley Rich

Ashley is a PHP and JavaScript developer with a fondness for solving complex problems with simple, elegant solutions. He also has a love affair with WordPress and learning new technologies.

  • Thanks for this. I followed the default Digital Ocean tutorial when setting up my server but I suspect that it could some hardening.

    Cheers

  • harishchouhan

    Wow, Thanks for writing this Ashley. This by far has been the most detailed. I look forward to your next part.

  • That was easy… thanks!

  • Awesome

  • Renato Alves

    Hell of an introduction. Loving it! =)

  • O

    Awesome articales/rundowns! Pretty easy to follow for a newbie like me!

    In regards to security and using Fail2ban….
    What’s your reason for using Fail2ban instead of OSSEC, psad and fwsnort instead?
    Read in several locations that that would provide superior security.
    Just curious 🙂
    Thx

    • Thanks!

      Fail2Ban, UTW and SSH hardening usually provide enough protection while remaining relatively straightforward to configure. OSSEC and other full blown intrusion detection systems can seem overkill for the majority of servers and add unnecessary complexity. If you’re hosting sensitive data, then they’re likely a good solution, but this series isn’t aimed at those situations.

      • O

        Thanks for the quick and clear reply! 🙂

  • Lu

    Boss.

  • Justin Samuel

    Hi Ashley, this is a great series of article for learning to manage servers. Nice work.

    For production WordPress hosting on DigitalOcean, have you tried out ServerPilot?

    https://serverpilot.io/

    If you like to do the server configuration and administration yourself, you wouldn’t want to use it. But it can be very useful if you’d rather skip the server admin work and get straight to hosting on DigitalOcean.

    • I haven’t personally tried ServerPilot, but I’ve heard good things. It definitely seems like a good alternative if you don’t want to concern yourself with server configuration.

  • I am using AWS EC2 servers at http://hackpundit.com for running my blog. I am on free tier. Can you recommend some security tips?

  • Bill Mandude

    How do I keep the key from generating in the home directory. It generates in the home directory and then it won’t work correctly I see you generated it in the users directory.

  • Kelly Conger

    prior to installing ufw and Fail2ban I was able to connect from both my windows and mac computers via ssh with a saved private key (windows/putty will ask for my private key passphrase MAC allows me to save it and autologon) AFTER installing ufw (I verified that ssh,http and tcp 443 are allowed) and fail2ban I had to revert to using my username password to connect? “Server refused our key” is the message displayed now. Anyone else having the same problem?

  • Teo Lopez

    I have followed the steps in the series. However the issue I am having is that at one moment I see my website I setup and then I see the coming soon page that Hover has setup I let some time pass and then I see my website again. Its been on going for a few days now since I noticed it. I am not sure if there is anything else I need to change on Hovers end or perhaps I made a mistake setting up nginx.. Any help is appreciated.

  • Amazing! Love the information you provide and will definitely apply it!
    BTW, is it me or does Digital Ocean have an article very very similar to this one?
    https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-lemp-on-ubuntu-16-04

    • Joe

      I recently completed that DO tutorial. It don’t recall “Setting the Hostname” or Fail2ban. Im having some issues ( with Nginx settings I think) so I am going through this tutorial to get a deeper understanding of my server setup.

  • Have been self hosting for many years now. At first I was doing it from my house with a commercial ISP account but changes in my life cause me to have to change that arrangement. This is a hobby for me I used to use Moveable Type until they decided to adopt the complete commercial model leaving at least for me (a hobbyist) WordPress the most viable choice. I moved to VPS style hosting last year and in my case a Linode setup. I am still an Apache user but since this is only a hobby Nginx looks interesting maybe eventually when I have some time i’ll try it. Also your plugin WP Offload S3 definitely helped me take my sites to another performance level (THANX!). Also I find your emails about web topics very helpful. With your help I have gotten through learning how to setup Amazon S3 and Cloudfront, how to enable two factor authentication for my VPS, and implement a HTTPS site. My current setup is Linode / Ubuntu Server / Apache / PHP / Mysql / Postfix (send only), again thank you for your continuing efforts to distribute pertinent information about site and server management.

    • Cheers Dale, I’m glad you’re finding the content helpful!

  • Steven

    Cheers for the update however there are some additional steps needed when setting up Fail2Ban with UFW. Unless this has since changed?…

  • Caleb Smith

    I’ve been reading through this series and it has been amazing to validate my sever knowledge and keep it super relevant to a WordPress setup!

    Would you recommend to setup every site in their own VPS over a shared server setup? For example – We are currently hosting 7 clients in one instance on a Siteground server (going to move over to a self-managed VPS soon). Would it be best to plan out for X site to be on X server only for separation? The cost isn’t really an issue with most sites only needing a small droplet on Digital Ocean.

  • Andrey

    You are the BEST!!!! Thank you so MUCH!!!!!!!!!!!!!!

  • Hi Ashley, Great article you have here!
    If you use RunCloud.io, we have these configuration figured out for you so you can focus on what’s more important – development!
    Check out our 15 days free trial ;D

    RunCloud – Create. Connect. Deploy | https://runcloud.io

  • It’s worth adding the -y option to command to install fail2ban, because a fresh install will most likely need to also install python.

    sudo apt-get install fail2ban -y