How to setup a VPS (or Dedicated Server) with Nginx from scratch

In this tutorial we are going to go through the steps of securing the server and configuring Nginx, PHP, Phpmyadmin and MySql.

Step 1. Basic Security

Assuming you have already logged into your server we start by changing the root password to something more secure.



This will prompt you to change the root password, It is recommended to choose something extremely complex as it is your most crucial line of defence against the outside world.

Next, open up the ssh config

vim /etc/ssh/sshd_config

and change the port from 22 to anything between 1025 to 65536, this will prevent our server from being easily spotted by malicious eyes

Port 22

We can now restart the ssh service to apply our changes

service ssh restart


Now that these basic tasks are done we need to update our server to the latest stable LTS. We can do this with

apt-get update
 apt-get upgrade

Step 2. Even more Security – Denyhosts

To make it even harder for those malicious individuals to compromise our server we are going to install denyhosts. This is a program that basically scans your log files an auto-bans anyone who seems a bit too suspicous.

apt-get install denyhosts

The default options are fine but I like to edit the configuration file and tweak the settings below

vim /etc/denyhosts.conf
BLOCK_SERVICE  = sshd    # Change to ALL
 DENY_THRESHOLD_INVALID = 5    # Change to 3
 DENY_THRESHOLD_VALID = 10    # Change to 3
 DENY_THRESHOLD_ROOT = 1    # Change to 3
 #RESET_ON_SUCCESS = yes    # Uncomment this line

What we have basically done is to make it easier to block individuals who try random attacks and give ourselves more chances to get it right.. I live in a country with ever changing IP Addresses and it’s annoying to get blocked from your own server when you enter the wrong password once.

If you have a static ip, then follow the step below, if not, please ignore

vim /etc/hosts.allow

tell denyhosts to ignore your IP by adding it to a whitelist in the format

ALL: 177.636.375.433    # Replace this with your static IP

When you are done, start denyhosts with

service denyhosts restart

(ps, if your ever want to see who is currently banned, check the /etc/hosts.deny file.)


Step 3. PHP 5.5

Ubuntu comes with 5.3 (maybe 5.4 now) in its repository but we much prefer Php 5.5 as it has more features, is faster and well… is 0.2 better 🙂 . To get this we need to use an external repo.

apt-get install python-software-properties
 add-apt-repository ppa:ondrej/php5

Repository added, now to update and install with some common modules

apt-get update
 apt-get install php5-common php5-mysqlnd php5-xmlrpc php5-curl php5-gd php5-cli php5-fpm php-pear php5-dev php5-imap php5-mcrypt

I have found that sometimes mcrypt requires a restart of the server to work properly, so if you get any ‘mcrypt’ errors, please ‘shutdown -r now’

if you wish to check your php modules and version info you can do so with

php -v    # version
 php -i    # Similar to running phpinfo()


Step 4. MySQL

When it comes to Mysql we have two options (actually, there are many but these are my favorite)

  1. Install percona server (A better, but heavier drop-in replacement for MySQL [suggested for hosts with at least 1GB of ram] )
  2. Install the default Oracle MySQL server (Machines with less than 1GB ram)

To install percona, we need to add the repo and then install. To do this..

apt-key adv --keyserver --recv-keys 1C4CBDCDCD2EFD2A
 echo "deb `lsb_release -cs` main" >> /etc/apt/sources.list.d/percona.list
 echo "deb-src `lsb_release -cs` main" >> /etc/apt/sources.list.d/percona.list
 apt-get update
 apt-get install percona-server-server-5.6 percona-server-client-5.6

For Oracle, as it is already in our repo… we just need to

apt-get install mysql-client mysql-server

Cool, almost here.. lets start the mysql server and secure it for production use

service mysql restart


Step 5. Installing Nginx

The normal Nginx Package tends to ignore a few packages I’ve grown to know and love and so I recommend you use either nginx-full or brian mercers nginx-custom. My preference is the custom because It has a few third party modules that even nginx-full does not (such as fcgi cache purge). To install, use

add-apt-repository ppa:brianmercer/nginx
 apt-get update
 apt-get install nginx-custom

If you wish to use nginx-full then you can just run

apt-get install nginx-full

NOTE. Please don’t try to install both at the same time..


Step 6. Configure, configure, configure


We first going to configure the php config file and make a few changes

  1. Prevent php from ‘guessing’ where users want to go (horrible security risk)
  2. Increase upload limit
vim /etc/php5/fpm/php.ini



replace with


Also, find and set the the following values as shown below

upload_max_filesize = 10M
 post_max_size = 12M



vim /etc/nginx/nginx.conf

we are going to make a bunch of changes here, try to make your config look like the following

user www-data;
worker_processes 2;    #should be equal to the number of cpu cores you have
pid /var/run/;
events {
 worker_connections 1024;  # Nginx can take alot of stress so we upped this
http {
sendfile on;
 tcp_nopush on;
 tcp_nodelay on;
 keepalive_timeout 65;
 types_hash_max_size 2048;
 # server_tokens off;
server_names_hash_bucket_size 64;    #to prevent errors with multiple Vhosts
include /etc/nginx/mime.types;
 default_type application/octet-stream;
fastcgi_buffers 8 16k;    #also to prevent erros
 fastcgi_buffer_size 32k;    # this too
client_max_body_size 12m;    # we also want to increase upload limit
access_log /var/log/nginx/access.log;
 error_log /var/log/nginx/error.log;
# We enable all the gzip options because google loves gzip
gzip on;
 gzip_disable "msie6";
gzip_vary on;
 gzip_proxied any;
 gzip_comp_level 6;
 gzip_buffers 16 8k;
 gzip_http_version 1.1;
 gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
 include /etc/nginx/conf.d/*.conf;
 include /etc/nginx/sites-enabled/*;


Now thats over with lets configure our Server Blocks (or Virtual Hosts)… we can either use the default file as a template for each seperate site or if you only have one site/you wish to use the ip directly feel free to simply edit the default file that is already there… In this example we are just going to edit he default

cd /etc/nginx/sites-availiable
vim default

Edit the host to resemble the following

server {
 listen 80;
root /usr/share/nginx/www;
 index index.php index.html index.htm;
server_name localhost;
location / {
 try_files $uri $uri/ /index.html;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
 location = /50x.html {
 root /usr/share/nginx/www;
location ~ .php$ {
 try_files $uri =404;
 fastcgi_pass unix:/var/run/php5-fpm.sock;
 fastcgi_index index.php;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 include fastcgi_params;

location ~ /.ht {
deny all;


Restart nginx 

service nginx restart

and go your domain (or ip) in your browser… Voilia!! A working nginx server.



IF you want another block to serve a different directory, copy the previous template but make the following changes

cp default myhost
vim myhost
root /usr/share/nginx/www/mynewhostdir;

Now that that’s done, lets make a link to it in our sites-availiable dir to activate it

ln -s /etc/nginx/sites-availiable/myhost /etc/nginx/sites-enables/myhost

Lets restart nginx again

service nginx restart

Aaaand done 🙂 (ps. your html/php files should be in the location specified by root option in the corresponding block.



Step 7. Phpmyadmin

What is sql without phpmyadmin? Luckily this last step is the easiest of them all

apt-get install phpmyadmin
ln -s /usr/share/phpmyadmin/ /usr/share/nginx/www

Now go to http://myipaddress/phpmyadmin

Questions? Comments? Fixes? Just leave em in the comments section?


You may also like...

Leave a Reply