Setup & Configure A LEMP Server

1. Choose A Linux Flavor. The first letter in L.E.M.P. stands for “Linux” – so before we begin configuring anything, we first must choose and install a linux distro. As this tutorial does NOT install any control panels such as cPanel/WHM, I highly recommend Ubuntu* as it is arguably the easiest distro to manage via shell (command line). Ubuntu, maintained by London-based Canonical Ltd., is renowned for its exceptional repositories and on-time release schedule, making server software updates impressively easy.

*Always use an LTS (Long Term Support) release when installing Ubuntu to a server for added stability and security. The most recent Ubuntu LTS version is Ubuntu 14.04

2. Root Login & Password. After you’ve installed Ubuntu to your server, login to your machine’s IP address via SSH using the root username (Note: if you are using Windows, connect to your server using a free SSH client such as Putty):

ssh root@

If you are logging in for the very first time, you’ll see a security message something like the one below. Simply type “yes” and hit enter to continue logging in:

The authenticity of host ' ('
can't be established. ECDSA key fingerpring is
12:34:45:1a:aa:12:33:4c:87:65:43:21:bb:3c:fa:c0. Are you sure you want
to continue connecting (yes/no)?

Depending on how you installed Ubuntu – i.e. if you installed it through your web hosting company’s automated server “imaging” interface – be sure to reset the default root password to something unique and secure if you haven’t done so already:


3. Add New User. After you’ve secured your root password, its time to create a new user with root privileges so that we no longer need to use the root user on our server. The prompt will ask you for things like “name” and “phone number” etc for your new user – simply skip through all of these fields unless you have a use for them:

adduser example

Next, we need to add “sudo” privileges to our new user, otherwise known as root privileges – this will allow our new user to do anything that root can do, as long as we prefix commands with “sudo” and input our password. The reason we don’t want to use root directly is to avoid accidentally inputting destructive commands – plus, we will later disable root SSH logins, creating additional security for our server.


Inputting the above command will let you edit the configuration file as below. After you’re done editing, hit CONTROL + X to exit and type “Y” to confirm saving the changes:

# User privilege specification
root    ALL=(ALL:ALL) ALL
example    ALL=(ALL:ALL) ALL

4. SSH Hardening. Now that you’ve setup a new user with root privileges, we can lock down SSH a bit which will greatly improve our server’s security:

nano /etc/ssh/sshd_config

The above command will bring you to another configuration file. Change the default port number for SSH to something between 1025 and 65536 to avoid common SSH attacks aimed at port 22. Although not foolproof, changing the port number adds an additional security layer against would-be attackers. After you’ve changed it, make sure you write it down or remember it, otherwise you’ won’t be able to connect to your server:

Port 55555

Next, disable SSH logins from user root to further protect your server:

PermitRootLogin no

Lastly, specify which users are allowed to login via SSH (WARNING: Check your spelling carefully! If you make a mistake on this step, you may lock yourself out!):

AllowUsers example

Once again hit CONTROL + X to exit and type “Y” to save changes. Now, restart SSH:

service ssh restart

5. Install Nginx Server. Here’s where you start feeling happy that you chose Ubuntu, because all the software we will be installing is conveniently available in Ubuntu’s default repositories. So, we can use the Ubuntu “shortcut” command called apt:

sudo apt-get update
sudo apt-get install nginx

On all new 14.04+ versions of Ubuntu, Nginx will automatically run after installation is complete. You are literally done installing Nginx with a single command.

6. MySQL Installation. You are probably aware that MySQL is the most popular database software in the world. In recent years, other software such as MongoDB has become popular among developers because of its speed and simplicity. However, the newest 5.6 version of MySQL has drastically improved performance compared with the older version 5.5 (its also important to note that newest MySQL version (5.6+) defaults to innoDB tables, which provides automatic table repair and optimization… make sure your database is compatible). Unless you are an advanced developer, stick with MySQL:

sudo apt-get install mysql-server-5.6

During setup, you will need to set a “root” MySQL password. Don’t be confused; this is not related to the SSH password for user root but rather like a “superadmin” password for MySQL that uses the username root to connect to MySQL.

Note: In pre-5.6 versions of MySQL the sudo mysql_install_db command is required to properly finish setting up MySQL. However after version 5.6 the command is no longer needed and should be skipped to avoid creating server errors.

Next, run the below security script to remove insecure MySQL permissions:

sudo mysql_secure_installation

Type “Y” for all of the options, which will greatly improve MySQL security:

Remove anonymous users? [Y/n] Y
Disallow root login remotely? [Y/n] Y
Remove test database and access to it? [Y/n] Y
Reload privilege tables now? [Y/n] Y

7. PHP-FPM Installation. So now we’ve got Nginx to serve our website files, and MySQL to store all of our data. But we need something to connect the dots and to generate dynamic content on the fly. Enter the magical world of PHP – specifically, PHP-FPM (fastCGI process manager) which allows Nginx to process PHP requests by passing them through FCGI. Lucky for us, this has added loading speed benefits:

sudo apt-get install php5-fpm php5-mysql

8. PHP Hardening. There’s a default setting when PHP is first installed that we must change in order to secure our server. First, enter the command below:

sudo nano /etc/php5/fpm/php.ini

Uncomment (remove the # symbol) and change the following line as below:


Performing this step will keep PHP from attempting to execute “nearest match” files when a path or file is missing. This prevents hackers from crafting malicious PHP requests in order to execute files they shouldn’t be allowed to access. Now, restart PHP:

sudo service php5-fpm restart

9. Configure Nginx Server Block. All of our server components are installed by now – however, Nginx still hasn’t been told to “handle” all of our dynamic content via PHP-FPM. For that, we use a “server block” which is similar to Apache virtual hosts:

sudo nano /etc/nginx/sites-available/default

I won’t spend time explaining all the various options and settings for Nginx server blocks. The below example is optimized for websites using WordPress as their CMS, and it provides both security and speed benefits:

server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;
        root /var/www;
        index index.php index.html index.htm;
        location / {
		try_files $uri $uri/ /index.php?q=$uri$is_args&$args;
		# try_files $uri $uri/ /index.php?$args; // or this
        location ~ .php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+.php)(/.+)$;
                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
                include fastcgi_params;
        location = /favicon.ico {
                log_not_found off;
                access_log off;
        location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
        location ~ /. {
                deny all;
                log_not_found off;
                access_log off;
	# comment this until WP config is properly setup (blocks access)
	# location ~* wp-config.php {
	#	deny all;
	# }
        location ~* /(?:uploads|files)/.*.php$ {
                deny all;
	location ~ .(eot|ttf|woff|svg|css)$ {
	    add_header Access-Control-Allow-Origin "*";
 	location ~* .(js|css|png|jpg|jpeg|gif|ico)$ {
                expires max;
                log_not_found off;
        error_page 500 502 503 504 /50x.html;
                location = /50x.html {
                        root /var/www;

The server block example above also specifies expires max on static files for improved cache control. It also properly configures @font-face directives to avoid display errors with Firefox/IE browsers. In the above example, /var/www is the default “web root” directory which is recommended for most websites. Now, restart Nginx one more time:

sudo service nginx restart

10. Setup MySQL Database. Even if you are migrating from another server, its usually easiest to simply setup a new MySQL database and later import your data into it:

mysql -u root -p
CREATE DATABASE newdatabase;
CREATE USER newuser@localhost IDENTIFIED BY 'newpassword';
GRANT ALL PRIVILEGES ON newdatabase.* TO newuser@localhost;

11. Migrate Server Data. If you are migrating your data from another server, simply use mysqldump to dump your old database into a single .sql file:

sudo mysqldump -u root -p [database name] > dump.sql

And use the powerful tar command to bundle all files in your old web directory:

sudo tar -czf migrate.tar.gz *

Use encrypted SCP to securely send your database and tarball to your new server:

sudo scp dump.sql user@
sudo scp migrate.tar.gz user@

On your new server, go to your web root i.e. /var/www and unzip the tarball:

sudo tar -xzf migrate.tar.gz

Now, restore your innoDB MySQL database to your freshly installed MySQL 5.6:

mysql -u root -p newdatabase < /path/to/dump.sql

12. Cleanup & Finish. We are nearly finished, but there are a few things we need to double check. First of all, make sure to configure your wp-config.php or other database configuration file depending on the CMS you are using. Also, let's make sure that Nginx owns the web root so that your CMS can update and edit files properly, and lastly, fix up any potential file + directory permission errors:

sudo chown -R www-data:www-data /var/www
find /path/to/your/wordpress/install/ -type d -exec chmod 755 {} ;
find /path/to/your/wordpress/install/ -type f -exec chmod 644 {} ;
sudo service nginx restart

Finally, enable all the gzip options on Nginx to compress your files and to speed up rendering times. On the newest versions of Nginx, the default options are usually fine:

sudo nano /etc/nginx/nginx.conf

Simply uncomment all of the default gzip options in order to activate them (below). And, like always, hit CONTROL + X to exit the nano editor and type "Y" to save changes:

# Gzip Settings

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;

CONGRATULATIONS! We're done. From here, you can install your favorites firewall(s) if you wish, such as fail2ban etc. Whenever possible, avoid installing an FTP server, email server, name server, or control panel to greatly speed up your server and avoid quite a few inherent security risks that accompany these unnecessary applications.

sudo service nginx restart

Leave a Reply