LEMP Stack Tutorial: Ubuntu + Nginx + MySQL + PHP-FPM Servers Are Impressively Simple Yet Powerful

If you’re a web developer of any sort, you’ve probably been overwhelmed to the point of frustration at least once during the past few years as more and more options continue to appear in the realm of web hosting and server configuration .

It was only a few years ago that practically the entire world was using the same Apache web server (with cPanel installed), and things like CDNs and DDOS attacks were not commonly discussed. But as web technology continues to grow at an exponential rate, independent designers and bloggers face growing learning curves in the world of web servers, as performance and scalability become evermore crucial in the face of fierce online competition for users’ respect.

In this tutorial, I will be showing you how to install a LEMP server stack from scratch. Although Apache (LAMP) servers will probably never die out – at least not for a while – the platform continues to lose ground to other players. In particular, many corporate + enterprise networks continue to choose Microsoft servers, and Nginx continues to grow rapidly among high traffic content portals and other websites.

How To 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@123.123.123.123

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 '123.123.123.123 (123.123.123.123)' 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:

sudo passwd

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:

sudo adduser example

Make sure your root user account (or other) and password never expire:

sudo chage -E -1 -m 0 -M -1 -I -1 -W 99999 root

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.

sudo visudo

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:

sudo 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

If you want to lock down your SFTP users and block SSH, comment this line…

# Subsystem sftp /usr/lib/openssh/sftp-server

…and then implement this at the very VERY bottom:

Subsystem sftp internal-sftp Match User example ChrootDirectory /home/example ForceCommand internal-sftp AllowTcpForwarding no X11Forwarding no

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

service ssh restart

Lastly it is recommended to set pretty much all servers across the world to be UTC timezone now, formerly known as either GMT or “Universal” time:

sudo dpkg-reconfigure tzdata

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 add-apt-repository ppa:nginx/development sudo apt-get update sudo apt-get install nginx-extras sudo apt-get update sudo apt-get upgrade

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 .

You will probably need unzip at some point, so install that too:

sudo apt-get install unzip

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

(or below if you are on Ubuntu 16.04 and wish to install MySQL 5.7)

sudo apt-get install mysql-client mysql-server

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

sudo apt-get install php5-gd php5-curl php5-mcrypt php5-mbstring php5-xml php5-json php5-soap

…(or if you are on Ubuntu 16.04 and want PHP7):

sudo apt-get install php7.0 php7.0-fpm php7.0-mysql php7.0-gd php7.0-curl php7.0-mcrypt php7.0-mbstring php7.0-xml php7.0-json php7.0-soap

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

(or)

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

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

cgi.fix_pathinfo=0

memory_limit = 256M

max_input_vars = 3000

post_max_size = 256M

upload_max_filesize = 256M

expose_php = Off

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

(or)

sudo service php7.0-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:

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, creating a user for both localhost and loopback IP:

sudo mysql -u root -p CREATE DATABASE wordpress; CREATE USER 'example'@'localhost' IDENTIFIED BY 'newpassword'; CREATE USER 'example'@'127.0.0.1' IDENTIFIED BY 'newpassword'; GRANT ALL PRIVILEGES ON wordpress.* TO 'example'@'localhost'; GRANT ALL PRIVILEGES ON wordpress.* TO 'example'@'127.0.0.1'; FLUSH PRIVILEGES; exit

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@123.123.123.123:path/to/send/database/ sudo scp migrate.tar.gz user@123.123.123.123:path/to/send/tarball/

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:

sudo 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 root:root /home/example sudo chmod 755 /home/example sudo mkdir /home/example/www sudo addgroup wordpress sudo adduser example wordpress sudo adduser www-data wordpress

Next reset file permissions or try this script instead:

sudo chown -R example:wordpress /home/example/www sudo find /home/example/www/ -type d -exec chmod 775 {} \; sudo find /home/example/www/ -type f -exec chmod 664 {} \; sudo find /home/example/www/wp-content/cache/ -type d -exec chmod 777 {} \; sudo find /home/example/www/wp-content/cache/ -type f -exec chmod 777 {} \; sudo chmod 660 /home/example/www/wp-config.php sudo chmod -R g+s /home/example/www/ sudo service nginx restart

For good measure, add this to wp-config.php:

/* make sure WordPress has enough memory */ define('WP_MEMORY_LIMIT', '256M'); /* make sure WordPress admin has enough memory */ define('WP_MAX_MEMORY_LIMIT', '256M'); /* avoid annoying FTP prompt screens on updates/installs */ define('FS_METHOD', 'direct');

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:

Please visit: Nginx Configuration Tuning

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 exit

Comments? Leave your intelligent feedback down below or consider following CollegeTimes on Facebook or Twitter to stay updated or to get in touch!

Share This Story: