LEMP Stack Setup Guide with Firewall and FTP Configuration

Viewer Rating

The Complete LEMP Stack Setup Guide For Beginners (Download PDF)

LEMP Stack Setup

L – Linux (Ubuntu 18.0.4 LTS)

E – NGINX (NGINX 1.14.0)

M – MySQL (MySQL 14.14 Distribution 5.7.29)

P – PHP (PHP7.2)


1 VPS Server: 4 GB RAM, 2 vCPU (AWS), 100 GB Hard Disk


1 Virtual Box machine: 4 GB RAM, 100 GB Hard Disk

Virtual Box Installation

Download and install the Virtual Box setp for Ubuntu Linux (64-bit).


Download and install the Virtual Box extension pack for the corresponding software version.


Ubuntu (64-bit) OS Installation

Download the iso image file for the preferred version of Ubuntu Linux Server Edition.



  1. The server edition does not have a GUI interface.
  2. Create a new virtual machine. Open its settings and select the storage option.
  3. Click on Controller IDE and add an optical drive (left-most circular button).
  4. Select the Ubuntu iso image file and save all settings.
  5. Start the virtual machine to initiate the installation process.

The iso image shall be automatically unmounted at the end of the installation process.

LEMP Stack Installation

1. Start the VM and login to the server instance.

2. Create a non-root user account (matrix) with sudo privileges.

root user privileges

home directory structure

3. Install the NGINX web server.

In order to display web pages to our site visitors, we are going to employ Nginx, a modern, efficient web server. All of the software used in this procedure will come from Ubuntu’s default package repositories. This means we can use the apt package management suite to complete the necessary installations.

sudo apt update

sudo apt install nginx

On Ubuntu 18.04, Nginx is configured to start running upon installation.

Check if the ufw service is running on the server. ufw is the Ubuntu Firewall system.

ps-ef|grep ufw

sudo ufw status

4. If not enabled, enable ufw as follows:

sudo ufw enable

5. If we have the ufw firewall running, we will need to allow connections to Nginx. Since we have not yet setup SSL on our VM, we only need to allow traffic on port 80.

sudo ufw allow ‘Nginx HTTP’

Check the status of ufw.

sudo ufw status

6. Check if the NGINX web server is running by accessing the ipaddress of the Guest OS (VM) from a browser on the Host OS.



ifconfig|grep inet

inet (dynamic ip address of VM)

Type this ip address from a browser installed on the Host OS.

If we see the above page, we have successfully installed Nginx.

Note: Please ensure the VM adapter type under network settings is set to Bridged Adapter.

Select an appropriate adapter name and test by accessing the ip address of the VM.

Set Promiscuous Mode to Allow All. (Not mandatory)

Restart the NGINX server: sudo systemctl restart nginx

Stop the NGINX server: sudo systemctl stop nginx

Start the NGINX server: sudo systemctl start nginx

Check NGINX version: nginx -v

7. Install the MySQL database server.

sudo apt install mysql-server

8. Secure the MySQL installation

sudo mysql_secure_installation

Error encountered.

Fix: Start the MySQL database service.

/etc/init.d/mysql start

Re-run the security script.

Do not activate the component: VALIDATE PASSWORD PLUGIN

Set the database root password: <Specify a strong password>

  • Remove anonymous users.
  • Disallow remote root login.
  • Remove the test database.
  • Reload privilege tables.

Stop the database: /etc/init.d/mysql stop

Check database status: /etc/init.d/mysql status

Check the authentication string.

sudo mysql (logs in without prompting for password)

SELECT user,authentication_string,plugin,host FROM mysql.user;

Default authentication for root is set to auth_socket.

This enables greater security than using a password but may complicate things when using an external program such as phpMyAdmin.

Change the authentication method to mysql_native_password.

ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘<enter password>‘;

Run FLUSH PRIVILEGES; from the MySQL prompt which tells the server to reload the grant tables and put the new changes into effect.

Sometimes, due to a MySQL bug, the alter user operation may not succeed. In such a scenario, remove the quotes around the ‘root’@’localhost’ string.


ALTER USER root@localhost IDENTIFIED WITH mysql_native_password BY ‘<enter password>‘;

(MySQL is case-insensitive. However, on some system and setup tables, case-sensitive commands may be required. In such a situation, run the same command in lower-case alphabets.)

Verify the authentication method after running the flush statement.

sudo mysql shall no longer allow automatic login. We have to login with a password.

mysql -u root -p

9. Install PHP and configure NGINX to use the PHP processor.

We have Nginx installed to serve our pages and MySQL installed to store and manage our data. However, we still do not have anything that can generate dynamic content. This is where PHP comes into play. Since Nginx does not contain native PHP processing like some other web servers, we will need to install php-fpm, which stands for “fastCGI process manager”. We will tell Nginx to pass PHP requests to this software for processing.

Depending on our cloud provider, we may need to add Ubuntu’s universe repository, which includes free and open-source software maintained by the Ubuntu community, before installing the php-fpm package.

sudo add-apt-repository universe

Install the php-fpm module along with an additional helper package, php-mysql, which will allow PHP to communicate with our database backend. The installation will pull in the necessary PHP core files.

sudo add-apt-repository universe

sudo apt install php-fpm php-mysql

We now have the LEMP stack components installed. However, we need to make a few configuration changes in order to tell Nginx to use the PHP processor for dynamic content.

This is done at the server block level (server blocks are similar to Apache’s virtual hosts). To do this, open a new server block configuration file within the /etc/nginx/sites-available/ directory.

cd /etc/nginx/sites-available/

ls -ltr

Take a back-up of the default configuration file in the home folder of the logged-in user. The default file is the default server block (site) of the NGINX server.

The default root location from where NGINX servers files is /var/www/html.

A symbolic link is present in the sites-enabled folder.

Whatever server block file is available in the sites-enabled folder shall be treated as an active website.

10. Test the configuration by creating a PHP file.

Create a php file called info.php with the following contents:




sudo nano info.php

Place it in the /var/www/html folder.

Error: The info.php file is prompted for download instead of showing the php server information.

Check the version of php-fpm running: ps -ef|grep php

Check the status of php7.2-fpm: sudo systemctl status php7.2-fpm

Test the NGINX configuration file for syntax errors: nginx -t

Several errors are listed. Check with: sudo nginx -t

Error: unexpected end of file, expecting “;” or “}” in /etc/nginx/sites-enabled/default:92

Check the NGINX php settings.

cd /etc/php/7.2/fpm

The php.ini file is our concerned file. No change required.

Edit the default server block file: /etc/nginx/sites-available/default

Uncomment the following 4 lines:

location ~ \.php$ {
include snippets/fastcgi-php.conf;


fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; // Check version


} // This is very critical. Do not miss. It shall throw errors in ‘nginx -t’ command if left uncommented and php shall not work properly.

Restart the NGINX service: sudo systemctl restart nginx.service


sudo systemctl restart nginx

Check nginx config erros again: sudo nginx -t

Check the url:

PHP is working fine if the above page is displayed. After testing, remove this file as it may provide valuable information about your configuration to unauthorized users who may use it to hack into your system. This file should not be present in Production systems: sudo rm /var/www/html/info.php

Enable Shared Folders between Host and Guest OS

Enabled shared folders. Virtual Box: Devices –> Shared Folders

  • Specify the full directory path to be mounted.
  • Select Auto-mount
  • Select Make Permanent

Auto-Mount through Virtual Box Manager

In case we enabled auto-mounting on creating a shared folder from the Virtual Box Manager those shared folders will automatically be mounted in the guest with mount point /media/sf_<name_of_folder>. To have access to these folders users in the guest need to be a member of the group vboxsf.

sudo usermod -aG vboxsf userName

The guest will need to restart to have the new group added.

Note: In certain cases, the auto-mount feature may not work due to system bugs or version incompatibility. In such situations, please setup and use ftp for file transfer.

Monitor System Usage

top (RAM and CPU utilization)


free -m

OR free -h (h – human readable format)


watch -n 5 free -m (refresh data every 5 seconds)


watch -n 5 free -h

df -k . (Disk space utilization)

Set a static ip adress for the virtual machine (Virtual Box)

Open Virtual Box –> File –> Host Network Manager

Click on Create to add a Host-Only network.

Click on Properties.

Note the Ipv4 and Network Mask addresses.

DHCP Enable check-box is kept unchecked by default. Leave it unchanged. Close the window.

Note: The host computer’s ip address (connected to dyamic ip-based router) shall not match the ip address of the guest virtual machine. The guest ip address may change after every reboot. Hence, we need to set a static ip address of our choice to proceed with the configuration of web and database servers on our virtual machine.

Select the virtual machine in the Virtual Box Manager window. Click on Settings – Network.

Adapter 1 should already be set up. Select Adapter 2.

Fill out the settings as shown below. Here, we specify the host-only adapter that we created earlier.

Enable Network Adapter: Checked

Attached to: Host-only Adapter

Name: vboxnet0 (Created earlier)

Other values should be pre-filled. Leave them unchanged.

Start the virtual machine.

ifconfig -a

This shows the network interfaces and ports. We shall work with the second one: enp0s8

sudo vim /etc/network/interfaces

Quit the file without making any change. In Ubuntu 18.04 (Bionic) we use netplan instead of network interfaces.

cd /etc/netplan/

ls -ltr

check the already existing files.

We need to configure the *.yaml file for a static ip address.

Take a backup of the original file.

Switch to root user: sudo su

cp -rp 50-cloud-init.yaml orig-config.yaml

Check all interfaces.

ip link show

We shall now configure orig-config.yaml and set enp0s8.

vi orig-config.yaml

Make the necessary changes as shown below:

addresses: [] // 102 is the variable part. 24 is the range of ip(s) available.

dhcp4: no

gateway4: // Noted earlier

Save the file and exit. Restart the network service.

sudo netplan apply

Check the changes in ip address.

Test by pinging the guest server from the host machine.


Not pinging. Restart the virtual machine and test again.

The virtual machine (guest) can now be successfully pinged from the host machine.

Check if the NGINX web server can be accessed from the host machine by typing the static ip address.

The static ip configuration has been successfully tested.

Setup ftp server on host and guest OS

Check for existing installed ftp processes:

dpkg -l|grep ftp

Install the ftp server:

sudo apt-get install vsftpd

Check sftpd server status after installation:

sudo service vsftpd status

The above screenshot shows the vsftpd service running on the host machine.

If the service is not active, please start it as follows:

sudo service vsftpd start

Test the connection with: ftp localhost

Login should be successful. Perform the same installation steps on the guest os as well.

Check the firewall status in the guest machine and allow ftp access from the host machine to the guest machine.

sudo ufw status

sudo ufw allow ftp

sudo ufw status

Test ftp access from the host machine to the guest machine.

Note: Host-Only adpater is enabled in the Virtual Box network configuration. The specified static ip address may be used to connect to the ftp server.

Enable write access in the guest machine through ftp.

sudo vim /etc/vsftpd.conf

Uncomment the following line: #write_enable=YES

Save and quit the file.

sudo service vsftpd restart

Transfer a file from the host machine to the guest machine.

Common ftp commands:

ls – List the direectory contents in the target ftp server.

cd – Change directory in the target ftp server.

lcd – Change directory in the source (local) server.

(m)put – Write/transfer file(s) to the target ftp server.

(m)get – Read/Transfer file(s) from the target ftp server.

bye – Exit the ftp prompt.

bin – Switch to binary mode; suitable for binary-content transfer.

ascii – Switch to ASCII mode; suitable for text-content transfer.

Check if the file transfer has been successful in the guest machine.

Note: ftp access through the static ip address might not work if the host machine is connected to the wifi router for internet connection in bridged adapter mode. In such a situation, use the other dynamic ip address which is shown by the following command: ifconfig|grep inet

Install phpMyAdmin for LEMP on Ubuntu

phpMyAdmin is a browser based GUI interface to manage and operate MySQL databases.

sudo apt-get update

sudo apt-get install phpmyadmin

Press tab and do not select either Apache or Lighttpd. Select OK and hit enter to continue with the installation.

Select Yes and hit Enter.

Enter the root password for the MySQL database installed earlier.

Re-enter the database root password to confirm and and continue with the installation.

We shall need to create a symbolic link between phpMyAdmin and the site’s root directory. If we are using the NGINX default root directory, use the following command:

sudo ln -s /usr/share/phpmyadmin/ /var/www/html

Test by accessing the url:

Error: 403 Forbidden

Check the folder permissions of /usr/share/phpmyadmin/ and /var/www/html/. This should be fine by default and no change is necessary. If not, rectify the same.

cd /etc/nginx/sites-available/

sudo vi default

Add index.php as a supported index in the site config file.

Save the changes and quit. Restart the NGINX web server.

sudo systemctl restart nginx

Test the public-ip-address/phpmyadmin URL again. The phpMyAdmin login page should be displayed.

Login with user id as ‘root’ and the associated password.

Default list of user accounts as visible in phpMyAdmin:

Warning noted in import section of phpMyAdmin.

This warning is usually encountered when an older version of phpMyAdmin is configured with a more recent version of PHP.


1) Download the latest version of phpMyAdmin and update the software.


2) Make a small editin a config file to forcibly type cast an array data type with the count function.

File name: /usr/share/phpmyadmin/libraries/plugin_interface.lib.php

Search and find the following line:

if ($options != null && count($options) > 0) {

Change it to the following:

if ($options != null && count((array)$options) > 0) {

Login to phpMyAdmin and test the import option again.

Try to import a database into MySQL through phpMyAdmin.

Error: 413 Request Entity Too Large

We have to edit the NGINX configuration file to allow an upload of larger file size. The following changes shall increase the file upload size for all server blocks.

cd /etc/nginx/

sudo vi nginx.conf

Add the following line in the http section:

client_max_body_size 15M;

Save and quit. Reload the NGINX configuration. Restart is not necessary.

sudo systemctl reload nginx

Login to phpMyAdmin and re-attempt the import operation.


We now have to change the PHP settings for maximum file upload size.

cd /etc/php/7.2/fpm/

sudo vi php.ini

Update upload_max_filesize to 15M.

Update post_max_size to 15M.

Save and quit. Reload php-fpm.

sudo systemctl reload php7.2-fpm

Re-attempt the database import operation.

Error: 504 Gateway Time-out

This usually happens if the database import file is large in size and can be fixed by suitably increasing the NGINX time-out values.

MySQL database operations

mysql -u root -p

create database knowhowspotdb;

create user ‘khuser’@’localhost’ identified by ‘<enter password>’;

use knowhowspotdb;

grant all privileges on knowhowspotdb.* to ‘khuser’@’localhost’;


cd /home/matrix/ftpbase

sudo apt install unzip

unzip a1034c7b8_knowhowdb_2020-01-24_10-13-50.sql.zip

mysql -u khuser -p knowhowspotdb < a1034c7b8_knowhowdb_2020-01-24_10-13-50.sql

Note: Ensure that the sql dump has been ftp’ed in binary mode and not in ascii mode.

Setup NGINX Server Blocks (Virtual Hosts)

When using the Nginx web server, server blocks (similar to the virtual hosts in Apache) can be used to encapsulate configuration details and host more than one domain off of a single server.

Nginx, on Ubuntu 18.04, has one server block enabled by default. It is configured to serve documents out of a directory at /var/www/html. This shall work well for a single site. However, we shall need additional directories if we want to serve multiple sites. We can consider the /var/www/html directory as the default directory that will be served if the client request does not match any of our other sites.

We will create a directory structure within /var/www for each of our sites. The actual web content will be placed in an html directory within these site-specific directories.

sudo mkdir -p /var/www/knowhow.com/html

The -p flag tells mkdir to create any necessary parent directories along the way.

We shall now reassign ownership of the web directories to our normal user account. This will let us write to them without sudo.

Note: Depending on our needs, we may need to adjust the permissions or ownership of the folders again to allow certain access to the www-data user. Dynamic sites will often need this.

sudo chown -R $USER:$USER /var/www/knowhow.com/html

We are using the $USER environmental variable to assign ownership to the account that we are currently signed in (Please ensure that we are not logged in as root). This will allow us to easily create or edit the content in this directory.

Copy the website’s files inside the html folder of the target site.

cd ~/ftpbase

cp -rp knowhowspot.zip /var/www/knowhow.com/html

unzip knowhowspot.zip

Now, we have to create a server block file for knowhow.com. Let us start by creating a copy of the default block file.

cd /etc/nginx/sites-available

sudo cp default knowhow.com

Edit the knowhow.com server block file.

sudo vi knowhow.com

Remove the commented lines.

Remove the default_server tags.

Update the file root path to /var/www/knowhow.com/html.

Update the server_name tag to include knowhow.com and www.knowhow.com.

Save and close the file. The final server block configuration file should look as shown below.

Now that the server block file is created, we have to enable it by creating a symbolic link from this file to the sites-enabled directory.

sudo ln -s /etc/nginx/sites-available/knowhow.com /etc/nginx/sites-enabled/

The knowhow.com server block shall respond to requests for knowhow.com and www.knowhow.com. We can create more server blocks for multiple and different websites. The default server block shall respond to any request on port 80 that does not match any enabled server block.

In order to avoid a possible hash bucket memory problem that can arise from adding additional server names, we will go ahead and adjust a single value within our /etc/nginx/nginx.conf file.

sudo vi nginx.conf

Within the file, find the server_names_hash_bucket_size directive. Remove the # symbol to uncomment the line:

# server_names_hash_bucket_size 64;

Save and quit the file.

Check for NGINX syntax errors.

Restart NGINX: sudo systemctl restart nginx

Modify the local host’s file for testing. This will not allow other visitors to view our site correctly, but it will give us the ability to reach each site independently and test our configuration.

sudo vi /etc/hosts

Add the following entry in a new line: knowhow.com www.knowhow.com

Save and quit the file.

This will intercept any request for knowhow.com and www.knowhow.com and send them to our server, which is what we want if we do not actually own the domains that we are using for testing purposes.

Test the website by accessing the following url in the browser of the local host machine (outside vm):

http://knowhow.com OR http://www.knowhow.com

Error: 404 Not Found

Edit the server block configuration file for knowhow.com.

In the location block, change the following:

try_files $uri $uri/ =404;


try_files $uri $uri/ /index.php?q=$uri&$args;

Save and quit the file. Restart NGINX server.

The first try_files directive means that if a file or directory does not exist, the web server shall throw a 404 error. The second directive redirects all requests to index.php. This is commonly used for software packages such Drupal, Joomla and WordPress.

Test the website again: http://knowhow.com or http://www.knowhow.com

We can add as many different sites as we want on the same server, subject to performance constraints due to capacity. For each website, we need to create a separate server block file similar to knowhow.com under /etc/nginx/sites-available. For every new server block, amend the root path and server_name to host each website’s files in its separate directory structure.

Example: If we want to host an additional website called kitchen.com, the server block for that website should look something as shown below.

We must also ensure that the necessary symbolic link is created in the /etc/nginx/sites-enabled directory in order to activate the new website.

Restart the NGINX server after the new server block is enabled. For testing purposes, we need to add an additional entry in the /etc/hosts file in the local host machine.

Access the newly configured website from the browser of the local host machine: http://kitchen.com

If configured successfully, the new website should get displayed as shown below.

This completes the LEMP stack setup process on Ubuntu Linux.

Leave a Comment

%d bloggers like this: