Tutorial: LEMP on an Ubuntu Server

In a previous post we talked about setting up a development environment either by installing ‘plug-and-play’ XAMP solutions or running a virtual machine. In this article we will set up our own LEMP stack inside a virtual machine, using VMware Player. These instructions will also be useful if you’re not using a virtual machine: just skip the first steps.

1. Download and install VMware Player

We will use VMware Player (free for non-commercial use), so be sure to download it. At the time of writing, the latest version is 6.0.1. Install it in a directory of your choice. Note that when we will install our virtual machine we have an option to choose another directory, so our virtual machine’s directory is independent of where we’ve installed Player.

2. Download Ubuntu Server

The L in LEMP stands for Linux. In this setup we’ll be using Ubuntu Server, a popular Linux distribution without a GUI. To download it, go to the Ubuntu Server download page and look for the latest version. Currently, the latest version is 14.04. Download it and put it into a directory of your choice.

3. Create a new virtual machine

We’ve downloaded VMware Player and Ubuntu Server, so now it’s time to setup and launch our own virtual machine.

Start Player and select ‘Create a New Virtual Machine’. A Window should prompt you with a New Virtual Machine Wizard. Pick ‘Install from: Installer disc image file’ and point it to where you’ve downloaded the Ubuntu iso-file.

Configure your install information: Full Name (not that relevant), Username and Password. Since we’re using this as our non-public development environment we can use a simple easy to remember password. This would be different if our virtual machine is publicly accessible. I will use the username myuser in this example.

Name your virtual machine. This is the name that will be shown in VMware Player to identify your machine. I usually pick a descriptive name related to the goal of the machine. In this case: Ubuntu Server LEMP. You can also customize the location the machine will be saved to.

Pick the disk size you allow your virtual machine to use. Just use the recommended amount (20 GB). If you want to tweak your machine any further, choose ‘Customize hardware’. I like to configure my network connection to be a bridged connection. This way, the virtual machine is accessible by all the devices in my local network.

You might want to run VMWare’s Easy Install. Please note that not all OS ISOs support Easy Installation. Wait for the installation to finish and login.

If you want to move out of your virtual machine, press Ctrl+Alt.

4. Update apt-get packages

After launching our virtual Ubuntu machine, it’s time to update our package archives. In other words: we want to add remote directories (‘personal package archives’ or ‘ppa’) from which we can download the latest versions of certain software. We can do this by running the command:

sudo add-apt-repository [package-repository]

To check if this command works, just try typing sudo add-apt-repository. Chances are, we need to install another package first. If this is the case, the command line will show an error: ‘command not found’. The add-apt-repository command is dependent on the python-software-properties package, so we probably want to install that first using the apt package manager:

sudo apt-get install python-software-properties

Let’s assume add-apt-repository works. Now, we have to find and add the correct ppa’s to get updated software. If the developers don’t provide ppa’s on their website, a place to find ppa’s is Launchpad. Keep in mind that not all listed ppa’s are offered by the official developers of the packaged software. It could be that the developers don’t maintain a ppa, but an unrelated or related third party does. The risk is pretty low, but check if the person offering the ppa is reliable. For now, we will add the following ppa’s using the following commands (just press the up-arrow for a re-entering the previous command and change the last characters):

sudo add-apt-repository ppa:nginx/stable
sudo add-apt-repository ppa:ondrej/php5
sudo add-apt-repository ppa:ondrej/mysql-5.6

This will add recent repositories for nginx, php5, and mysql5.6. Note that the php5 and mysql-5.6 repo’s aren’t by the official developers. Ondřej Surý is part of the Debian pkg-php team, who create/maintain software packages for Debian (the Linux distro on which Ubuntu is based). To get php5.6 add ppa:ondrej/php5-5.6 .

Now we need to update our apt package manager, so that it uses these new sources:

sudo apt-get update

If everything went alright, we can continue by downloading packages and installing our software via apt.

5. Install and configure nginx

Nginx is our web server software. A webserver basically answers to HTTP requests by clients (such as a web browser), either by returning a file (on static requests, i.e. html-files) or by routing to a program that returns a file (on dynamic requests, i.e. php-files). A webserver has to be configured to do so.

Nginx is comparable to apache2, but it has my preference as it seems to perform better on lower budget machines out-of-the-box. I’ve also found it pretty easy to work with. Before installing nginx, we might need to uninstall apache2 first so there will not be a conflict:

sudo apt-get remove apache2

Then, let’s install nginx:

sudo apt-get install nginx

If the installation is succesfull, it will start nginx automatically. If not:

sudo service nginx restart

Now, we actually have a running webserver. To connect to it in our browser, we need to know the ip-address of our virtual machine. Find this by doing:


Look at the inet addr (not the local ‘lo’, so not, in my case it’s Go to your non-virtual machine (master) and type in this ip-address. We should see a ‘Welcome to nginx!’ page. If you don’t see this, make sure your virtual machine is on the network and nginx is running. Also, you might want to check the nginx error log:

nano /var/log/nginx/error.log

Now, we have to setup nginx to route our requests to a specific directory and use php5 (when we’ve installed it, of course). First create the directory where we want to put our files. Nginx defaults to /usr/share/nginx/html. I like to put it inside of the home directory of the logged in user (myuser): /home/myuser/www. So let’s create this.

mkdir /home/myuser/www

We need to setup nginx to route to this directory. We do this by setting up virtual hosts. These can be found in /etc/nginx/sites-available. The default settings are found in default, to check them do:

nano /etc/nginx/sites-available/default

To quit nano, do ctrl+x. Let’s create our own virtual host file:

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

If you want to create your own config, mind the common pitfalls. A pretty standard, yet detailed, configuration is the following:

# configure a basic virtual host for our webserver
server {
  # listen on port 80, default for HTTP
  listen 80;
  # when a request is received, automatically look for this file
  index index.php index.html;
  # configure our servername to be accessible from localhost
  server_name localhost;
  # look for files in this directory
  root /home/myuser/www;
  # setup error files
  error_page 404 /404.html;
  error_page 505 502 503 504 /50x.html;
  # route errors to default nginx errors
  location = /404.html{
    root /usr/share/nginx/html;
  location = /50x.html{
    root /usr/share/nginx/html;
  # if no path is found in the url, ...
  location / {
    # ... first try delivering the file at the url, then try giving access to a directory, then try serving an index.html
    # you might want to change this to index.php if you want nginx to fallback to index.php
    try_files $uri $uri/ /index.html;
  # if the path ends with .php 
  # NOTE: this can be a security risk when you allow users to upload files that contain code
  # i.e.: http://www.example.com/image.gif/attack.php; will run image.gif
  location ~ \.php$ {
    # NOTE: this will be problematic when you run nginx and php on different machines
    try_files $uri $uri/ =404;
    # Uncomment this to disallow dots in the name to prevent uploaded image exploits.
    # NOTE: you also can't use dots in scriptnames
    # location ~ \..*/.*\.php$ {return 404;}
    # setup php via fastcgi; Remember to set "cgi.fix_pathinfo = 0;" in php.ini
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    # pass to php5-fpm
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;

Exit (Ctrl+X) and save (‘Y’) nano. We have created our virtual host configuration, but we need to enable it. Let’s go to the sites-enabled directory, disable the default settings and enable our own.

cd /etc/nginx/sites-enabled
sudo rm default
ln -s /etc/nginx/sites-available/mysite mysite

The last command will create a symbolic (-s) link (ln) in sites-enabled (the current directory) from our file in sites-available  under the name ‘mysite’. Let’s restart nginx to see if it works.

sudo service nginx restart

If it fails, we have probably done something wrong. Look at the logs and try to fix it (nano /var/log/nginx/error.log). Most of the cases, it fails because of a typo or a missing bracket.

6. Install and configure php5-fpm

We should be ready to install php by now (… finally!). Let’s install the fast process manager variant, which works best with nginx, and the command line interface variant, which can work without a webserver.

sudo apt-get install php5-fpm php5-cli

We should test if php is running. Create a file in our directory with the phpinfo() function.

nano /home/myuser/www/phpinfo.php

To test if php and nginx are running correctly, go to <ip-address>/phpinfo.php in the webbrowser on our master device. In my case, that would be: If you see a big table of php-related information, everything went fine. If not, check the logs. Restart php5-fpm and nginx.

7. Install MySQL

We will install MySQL next. It consists of a client program (an interface for talking to the database) and a server program (the actual database). When installing, we will be asked to configure are root password for the database. Note that this is not the system’s root user, but the mysql server root user. Since this is not a public facing machine, just enter an easy password.

sudo apt-get install mysql-client mysql-server

That’s it. We now have a virtual LEMP stack installed.

8. Install vsftpd for a ftp-connection

Editing php-files or other files inside of the virtual machine can be a pain. What we could do, is install and setup vsftpd so we have a ‘virtual’ ftp connection to our server. That way we can use our favorite editor or IDE and upload files to our virtual machine.

sudo apt-get install vsftpd

Let’s use some workable settings for a server that is not public facing:

nano /etc/vsftpd.conf

Change the file so that you at least have the following settings, don’t forget to remove the hash-signs (#) in front of settings you want activated:


Do ‘service vsftpd restart’ and check if it works by logging in with FileZilla or another ftp-program. We can use the same credentials we use to login to the machine. If it doesn’t work, check the config file for bugs and read the logs.


Hurray! We now have a working LEMP stack with access via ftp. Among other subjects, we’ve covered:

  1. How to create a virtual machine with VMware Player.
  2. How to install Ubuntu on a virtual machine.
  3. Installing packages in Ubuntu and updating the package list.
  4. How to configure an nginx virtual host.
  5. How to setup ftp.
  6. Some basic linux commands.