The Ultimate Guide to Setting Up a WordPress VPS – Part 3

If you purchase through a link on our site, we may earn a commission. Learn more.

We're back for the third instalment of the VPS setup guide for WordPress. Just to refresh your memories, we are setting up a VPS using the VPS.net service, optimised for faster serving of your WordPress sites. In Part 1 we discussed the technologies used, and in Part 2, we set up Ubuntu, PHP, MySQL and phpMyAdmin. It is important that you follow step-by-step, so if you haven't completed the previous parts, head back to those posts before starting Part 3.
Table of Contents
WP Engine High Performance Hosting
BionicWP Hosting

We’re back for the third instalment of the VPS setup guide for WordPress. Just to refresh your memories, we are setting up a VPS using the VPS.net service, optimised for faster serving of your WordPress sites. In Part 1 we discussed the technologies used, and in Part 2, we set up Ubuntu, PHP, MySQL and phpMyAdmin. It is important that you follow step-by-step, so if you haven’t completed the previous parts, head back to those posts before starting Part 3.

Today we will be continuing where we left off in the last tutorial. At this point you should have a fully functional server with PHP and MySQL ready to start working and serving out your sites. Time to proceed in dabbling with the Nginx virtual hosts. In this part of the tutorial I will be following the excellent tutorial from themesforge.com very closely, touching on some extra points along the way.

1. Create a directory for your website

We will be storing websites in the /srv/www directory, although there are other places you can use, such as /var/www or /home/site/www. According to the Linux Filesystem Hierarchy Standard (FHS), the most appropriate place to place such files is in the /srv directory. More about this rationale can be found on MDSkinner’s site and in this thread on the Ubuntu forums.

Let’s create the directories needed for my fictional site mysite.com. You should replace this name with your own site.

[bash]
mkdir -p /srv/www/mysite.com/public_html
mkdir -p /srv/www/mysite.com/logs
[/bash]

The public_html folder will contain your website’s files while the logs folder is for log files generated by the system in relation to this site.

2. Create an Nginx virtual host file for your site

We will assume here that you will be running a number of domains on your VPS. So we will need to use virtual hosts to make that work. Before we begin, head over to /etc/nginx/nginx.conf, and check that this file (which is the main Nginx configuration file) contains the following options:

[bash]
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
[/bash]

With those two options enabled, Nginx will look for configuration files also in /sites-enabled/, which is where we will be setting up all our sites’ configurations. It is also a good idea to take a backup of the nginx.conf file, which can be done with the following command:

[bash]
cp nginx.conf nginx.conf.default
[/bash]

Now if you ever mess up nginx.conf you can stay relaxed because you would just need to copy the nginx.conf.default back to nginx.conf.

Excellent, we are now off to start configuring our first site with the domain mysite.com (as an example):

[bash]
cd /etc/nginx/sites-available/
touch mysite.com
[/bash]

Now open the file mysite.com

[bash]
nano mysite.com
[/bash]

and paste the Nginx config below, replacing references to mysite.com with your own domain name.

[bash]
# W3TC config rules based on
server {
listen 80; ## listen for ipv4; this line is default and implied
#listen [::]:80 default ipv6only=on; ## listen for ipv6
# Tell nginx to handle requests for the www.mysite.com domain
server_name www.mysite.com mysite.com;
if ($host != ‘mysite.com’) {
rewrite ^/(.*) permanent;
}
index index.php index.html index.htm;
root /srv/www/mysite.com/public_html;
access_log /srv/www/mysite.com/logs/access.log;
error_log /srv/www/mysite.com/logs/error.log;
# Use gzip compression
# gzip_static on; # Uncomment if you compiled Nginx using –with-http_gzip_static_module
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_buffers 16 8k;
gzip_http_version 1.0;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript image/png image/gif image/jpeg;
# Rewrite minified CSS and JS files
rewrite ^/wp-content/w3tc/min/([a-f0-9]+)\/(.+)\.(include(\-(footer|body))?(-nb)?)\.[0-9]+\.(css|js)$ /wp-content/w3tc/min/index.php?tt=$1&gg=$2&g=$3&t=$7 last;
# Set a variable to work around the lack of nested conditionals
set $cache_uri $request_uri;
# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
set $cache_uri ‘no cache’;
}
if ($query_string != "") {
set $cache_uri ‘no cache’;
}
# Don’t cache uris containing the following segments
if ($request_uri ~* "(\/wp-admin\/|\/xmlrpc.php|\/wp-(app|cron|login|register|mail)\.php|wp-.*\.php|index\.php|wp\-comments\-popup\.php|wp\-links\-opml\.php|wp\-locations\.php)") {
set $cache_uri "no cache";
}
# Don’t use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp\-postpass|wordpress_logged_in") {
set $cache_uri ‘no cache’;
}
# similar to Apache Status – handy for quickly checking the health of nginx
location /nginx_status {
stub_status on;
access_log off;
allow all;
}
# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
try_files /wp-content/w3tc/pgcache/$cache_uri/_index.html $uri $uri/ /index.php;
}
# Cache static files for as long as possible – removed xml as an extension to avoid problems with Yoast WordPress SEO plugin which uses WP rewrite API.
location ~* \.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
try_files $uri =404;
expires max;
access_log off;
}
# Deny access to hidden files
location ~* /\.ht {
deny all;
access_log off;
log_not_found off;
}
# Pass PHP scripts on to PHP-FPM
location ~* \.php$ {
try_files $uri /index.php;
fastcgi_index index.php;
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
}
}
[/bash]

The above configuration is one you can use on different sites and it is also optimised for the W3 Total Cache plugin (which we will be implementing in the next tutorial).

For a non-WordPress-specific config, you can use something like the following:

[bash]
server {
listen   80; ## listen for ipv4; this line is default and implied
#listen   [::]:80 default ipv6only=on; ## listen for ipv6

# Tell nginx to handle requests for the www.mysite.com domain
server_name         www.mysite.com mysite.com;

if ($host != ‘mysite.com’) {
rewrite ^/(.*)     permanent;
}

index           index.php index.html index.htm;
root            /srv/www/mysite.com/public_html;
access_log      /srv/www/mysite.com/logs/access.log;
error_log       /srv/www/mysite.com/logs/error.log;

# Use gzip compression
# gzip_static       on;  # Uncomment if you compiled Nginx using –with-http_gzip_static_module
gzip                on;
gzip_disable        "msie6";
gzip_vary           on;
gzip_proxied        any;
gzip_comp_level     5;
gzip_buffers        16 8k;
gzip_http_version   1.0;
gzip_types          text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript image/png image/gif image/jpeg;

# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
try_files $uri $uri/ /index.php?$args;
}

# Pass PHP scripts on to PHP-FPM
location ~* \.php$ {
try_files       $uri /index.php;
fastcgi_index   index.php;
fastcgi_pass    127.0.0.1:9000;
include         fastcgi_params;
fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param   SCRIPT_NAME        $fastcgi_script_name;
}
}
[/bash]

Now we need to symlink back to sites-enabled and restart Nginx:

[bash]
ln -s /etc/nginx/sites-available/mysite.com /etc/nginx/sites-enabled/mysite.com
service nginx restart
[/bash]

3. Download WordPress

Run the following commands to get a copy of the latest version of WordPress from WordPress.org, extract the files into the /public_html directory, and clean up the redundant files.

[bash]
cd /srv/www/mysite.com/public_html
wget https://wordpress.org/latest.tar.gz
tar -zxvf latest.tar.gz
cp -rvf wordpress/* .
rm -R wordpress
rm latest.tar.gz
[/bash]

Head over to phpMyAdmin and create a new database, we will use it while setting up WordPress.

4. Set Permissions

Another thing we need to do its to set permissions, make sure not to skip this step as it is one of the biggest culprits of confusion for new VPS users trying to set up their WordPress site.

[bash]
chown -R www-data:www-data /srv/www/mysite.ex/public_html
[/bash]

This command gives Nginx permission to write files to your web root (public_html). Nginx sets the main system user account to www-data, the same account Apache uses on Ubuntu.

5. Set Up DNS

Before you can navigate to your newly created site using the domain you’ve chosen, you first need to set up DNS. To manage your domains using the VPS.NET dashboard you need to change the nameservers of your domain. This is done at the domain registrar where you purchased your domain from. You will need to change your nameservers to:

  • dns1.vps.net
  • dns2.vps.net
Once that step is done head back to the VPS.net dashboard and follow this tutorial to complete the DNS setup for your new WordPress site.

Once that’s done it can take up to 48 hours for DNS to update and your domain to start resolving to the new server.

6. Install WordPress

Once the DNS stage is done and you’ve given DNS the necessary time to propagate, you should be able to type in your domain and see the first screen of the WordPress install process. Go ahead and complete the famous 5 minute install. Bravo!

Once you’ve installed your shiny copy of WordPress, I suggest you install the Nginx compatibility plugin.

The plugin solves two problems:

  1. When WordPress detects that FastCGI PHP SAPI is in use, it disregards the redirect status code passed to wp_redirect. Thus, all 301 redrects become 302 redirects which may not be good for SEO. The plugin overrides wp_redirect when it detects that nginx is used.
  2. When WordPress detects that mod_rewrite is not loaded (which is the case for nginx as it does not load any Apache modules) it falls back to PATHINFO permalinks in Permalink Settings page. nginx itself has built-in support for URL rewriting and does not need PATHINFO permalinks. Thus, when the plugin detects that nginx is used, it makes WordPress think that mod_rewrite is loaded and it is OK to use pretty permalinks.

The plugin does not require any configuration. It just does its work. You won’t notice it — install and forget.

That’s all for today, after completing the first 3 parts of our WordPress VPS setup tutorial, you now have a working WordPress website on your server. You can also follow today’s steps to add more websites.

In the next tutorial we will be focusing on speed optimising by implementing a host of caching methods including W3 Total Cache, APC Opcode cache and Varnish cache.

Hope you’ve enjoyed today’s guide, and as usual feel free to leave your comments and tips below.

If you enjoyed this post, make sure to subscribe to WPMayor’s RSS feed.

Jean Galea

Jean Galea is an investor, entrepreneur, and blogger. He is the founder of WP Mayor, the plugins WP RSS Aggregator and Spotlight, as well as the Mastermind.fm podcast. His personal blog can be found at jeangalea.com.

Discover more from our archives ↓

Popular articles ↓

14 Responses

  1. I cannot progress to step 3 (Download WordPress) because Nginx fails to restart. What would cause this?

    1. Can you make sure that you have all the semicolons in place? I had left this one out:
      fastcgi_pass unix:/var/run/php5-fpm.sock;

  2. It’s a great and easy tutorial indeed. I have been trying several tutorials from yesterday but I wasn’t able to access my site after installing WP into it. I was working on your tutorial step by step 🙂 … Finally I can access my site. Thank you very much for such a nice tutorial.

  3. Hi,
    After symlink back to sites-enabled and restart Nginx I get this error:
    nginx: [emerg] pcre_compile() failed: unmatched parentheses in “^/wp-content/w3tc/min/([a-f0-9]+)/(.+).(include(footer|body))?(-nb)?).[0-9]+.(css|js)$” at “).[0-9]+.(css|js)$” in /etc/nginx/s

    Where’s that parentheses? I can’t figure it out.

  4. Hey, are there any steps to get email working? I.e. password reset emails etc that wordpress sends using the php mail() function. I assume this uses sendmail or postfix but I didn’t see any mention of it in the guide. Otherwise, very helpful. Thanks

    1. It’s been a while since I’ve played around with this, but if I remember well email from WordPress itself should work fine.

      1. I’ve come back long after the time having set up a new Amazon EC2 instance and again noticed mail does not work out of the box on Ubuntu.
        It’s basically a case of installing sendmail/postfix (sudo apt-get install postfix) and it should work. Setting up SPF records for your domain is also worth mentioning.
        Your guide is very good but without the above users won’t be able to get confirmation emails/password resets etc.

  5. Hi, i just wanted to say thanks!
    I got my first VPS server and ONLY! after following your tutorial i finally managed to see my WordPress site working! So thank you! Thank You! Thank You!

    Now i want to try host multiple websites, i got about 20 to move them.
    Can you tell me how can i do this? what steps i need to repeat?

    1. Glad it helped Alex. Adding more websites shouldn’t be too difficult now that you added your first one. You’ve got the software running and it’s a case of creating new folders for the other websites, and setting up virtual hosts for them.

  6. Jean, THANK YOU for the awesome tutorials. Wanted share my changes to the above config.

    1. Rewrite config (This seems more direct…I was also dealing with some weird redirect loops…hard to say if the rewrite above was cause)

    server {
    # Redirect yoursite.com to www.yoursite.com
    listen 80;
    server_name yoursite.com;
    rewrite ^(.*) https://www.yoursite.com$1 permanent;
    }

    server {
    # Tell nginx to handle requests for the www.yoursite.com domain
    listen 80;
    server_name www.mysite.com;
    index index.php index.html index.htm;
    root /srv/www/mysite.com/public_html;
    access_log /srv/www/mysite.com/logs/access.log;
    error_log /srv/www/mysite.com/logs/error.log;

    # See rest of config…

    2. “catch all to route all requests to the bootstrap file” (see RavanH’s comment in

    # unless the request is for a valid file, send to bootstrap
    if (!-e $request_filename)
    {
    rewrite ^(.+)$ /index.php? last;
    }

    Before adding this, I was having trouble with a plugin that uses a file that ending in “css.php” (see

    3. Using W3-TC’s settings under General>Miscellaneous
    By adding my Nginx server configuration file path and removing the W3-TC settings in the tutorial config, I was able to get the exact plugin settings in my Nginx config.

    Would love to know your thoughts on these changes. I am by no means a programmer, but these 3 changes have really stabilized everything.

    Thank you again, Jean. Your tutorial inspired me to finally switch to a VPS!

    1. Thanks a lot Jonathan, I’m glad this tutorial inspired you to make the switch 🙂 Will also try out your settings!

  7. Congratulations on a great 3 part tutorial.
    I was really in need for this kind of instructions as I plan to buy a VPS and try and install a website myself.
    I’ll put the instructions in practice soon. If I fail I can always count on some sysadmin friends of mine to help me out 🙂
    Thanks.

Share Your Thoughts

Your email address will not be published. Required fields are marked *

Claim Your Free Website Tip 👇

Leave your name, email and website URL below to receive one actionable improvement tip tailored for your website within the next 24 hours.

"They identified areas for improvement that we had not previously considered." - Elliot

By providing your information, you'll also be subscribing to our weekly newsletter packed with exclusive content and insights. You can unsubscribe at any time with just one click.