Setting up LEMP stack for local development on Linux

Important note! This guide is made for setting up environment for local development. Don’t use this configuration on production environment as it can lead to security risks.

LEMP stack image

Linux is THE platform for developing web applications. I don’t want to start platform war, there are plenty of options that does the job well. But developing stuff on platform which is ran on production environment is the best choice for learning how stuff works. This guide will show how to install nginx, PHP and MariaDB (drop in MySQL replacement) and configure everything for local development. Major requirement is to have Ubuntu 16.04 or higher installed, or any other Ubuntu based distribution which uses apt-get as package manager. For the sake of simplicity, first we’re going to install everything, then we’re going to configure it to all work together. If you follow everything, you will have nice start for easy local development of PHP applications.

Installing server

First thing we’re going to install is HTTP server called nginx. It is popular server and I really like its configuration syntax. A lot of people use Apache which is a great option too. Anyway, first we need to update our package repositories and then install nginx. Open terminal and type following:

sudo apt-get update
sudo apt-get install nginx

Check if nginx is installed by typing nginx -v  in terminal. Output should look like this:

nginx version: nginx/1.10.0 (Ubuntu)

Also, when you type localhost in your web browser address bar you should see Welcome to nginx! message which confirms our HTTP server is working well.

There are few important things to know about nginx. Main configuration file is /etc/nginx/nginx.conf. Another important part is setting up server blocks (virtual hosts in Apache world). All of the available server blocks are located in /etc/nginx/sites-available directory. To activate available server block configuration we need to create symbolic link in /etc/nginx/sites-enabled. More on adding server blocks will be in upcoming sections of guide.

Installing MariaDB (MySQL drop in replacement)

Now we need to install database management system (DBMS). We choose MariaDB, which is modern relational DBMS and arguably better version of MySQL (of which it is fork). Type following command in terminal:

sudo apt-get install mariadb-server mariadb-client

Check if MariaDB is installed by running mysql –version.

Installing PHP

Next thing to install is PHP. Since we’re using newer version of Ubuntu (16.04 or above) default version of PHP in package repositories is PHP 7. We need to install PHP 7 with few extensions that are mandatory for modern web applications. Type following in terminal:

sudo apt-get install php php-fpm php-zip php-xml php-mbstring php-mcrypt php-curl php-gd php-mysql

Now run php -v  in terminal and you should see similar output:

PHP 7.0.8-0ubuntu0.16.04.3 (cli) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.8-0ubuntu0.16.04.3, Copyright (c) 1999-2016, by Zend Technologies

Configuring and wrapping up everything

There are a lot of guides how to install LEMP stack. Focus of this guide is on setting up everything for local development. Yes, you can work with default configuration but there are some caveats. When I develop my sites locally I don’t want to have file permission issues. I want that everything just works, no matter what I do. Since we are working on our personal computers, we can set up everything exactly as we want to. To avoid various file permissions issues, I want to serve my sites from my home directory. In my case, I prefer /home/ivan/projects as my directory of all web projects. I also don’t want to touch default file permissions of frameworks I use, such as Laravel. To do so, we need to run nginx as a current user, in my case that is ivan. Open terminal and type following:

sudo nano /etc/nginx/nginx.conf

Now we can modify nginx configuration. We need to change first line from user www-data  to user your_username. In my case I changed that line to user ivan. You can close and save file by pressing Ctrl+X. Now we need to restart nginx to reload new configuration by running sudo systemctl restart nginx.service or sudo systemctl reload nginx.service in terminal.

Configuring PHP to work with nginx

In order to PHP and nginx work together (in our home directory), we need to configure both of them. We need to run php-fpm as current user, and configure that it can work with server that is also ran under current user. To do so, run sudo nano /etc/php/7.0/fpm/pool.d/www.conf in terminal and change relevant lines to this:

user = your_username
group = your_username
listen.owner = your_username = your_username

Close and save with Ctrl+X and run sudo systemctl restart php7.0-fpm.service to reload new configuration.

Now let’s make nginx serve PHP apps. Run mkdir ~/projects and then  sudo nano /etc/nginx/sites-available/default and replace content with this:

server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;

        root /home/your_username/projects;
        index index.php index.html index.htm;

        server_name localhost;

        location / {
                try_files $uri $uri/ =404;
                autoindex on;

	location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;

    	location ~ /\.ht {
        	deny all;

Don’t forget to write your user name on relevant place. Close and save with Ctrl+X and run sudo systemctl restart nginx.service to reload new configuration.

Configuring MariaDB

This one is easy. Open your terminal and run mysql_secure_installation. Just follow all of the steps and you will properly set up MariaDB.

Checking if everything works

Let’s create index.php file in ~/projects directory and run phpinfo() function. Run printf “<?php\nphpinfo();\n” >> ~/projects/index.php to create and populate new file. Now check localhost URL in your preferred browser and you should see information about your PHP installation. That confirms that everything is set up correctly.

“Must haves” for development

Rest of the guide is not mandatory but it can make development much more easier and enjoyable.

Xdebug PHP extension

Xdebug is really useful PHP extension made for debugging purposes. To install Xdebug run the following:

sudo apt-get install php-xdebug
sudo sh -c "echo 'xdebug.remote_enable=1' >> /etc/php/7.0/mods-available/xdebug.ini"
sudo phpenmod xdebug
sudo systemctl restart php7.0-fpm.service

It’s up to you to integrate Xdebug into your IDE of choice.

phpMyAdmin (always up to date)

PhpMyAdmin is a great web-based DBMS interface. I prefer to install it by pulling latest version using git and using composer to install dependencies. Run sudo apt-get install git to install git and follow these instructions to install composer. Note for composer: it needs to be available as global binary so you can run it anywhere. Now run git clone ~/projects/phpmyadmin to get source files for phpMyAdmin. Then cd ~/projects/phpmyadmin && composer update –no-dev to install all of the dependencies. Getting phpMyAdmin git repository may take a while so be patient. When dependencies are installed you can go to http://localhost/phpmyadmin/ and use phpMyAdmin. Every time you want update phpMyAdmin you should run git pull && composer update  in terminal.

Server blocks

Let’s say you want to access your phpMyAdmin app by going to instead of http://localhost/phpmyadmin/. Perfect, it’s right time to show how to set up nginx server block. Run sudo touch /etc/nginx/sites-available/phpmyadmin . Now open that file and add following content:

server {
    listen 80;

    root /home/your_username/projects/phpmyadmin;
    index index.php index.html index.htm;


    location / {
        try_files $uri $uri/ /index.php?$query_string;

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

Next we have to tell nginx to load new configuration. We need to create symlink like this: sudo ln -s /etc/nginx/sites-available/phpmyadmin /etc/nginx/sites-enabled/. Then we have to restart nginx to activate new config by running sudo systemctl restart nginx.service.

Final step is to edit /etc/hosts file and add line. Now open and enjoy in your phpMyAdmin installation.


Improvements never stop. There are many ways how you can improve your set up. Consider this as a starting point for development without classic hassles. I would like to improve this setup by adding easy PHP version switching. Also, having this setup available for all users on machine would also be a plus. But right now, this should be enough for most beginner developers. Hope it helps you! Please comment if you have trouble with something. Thanks for reading.

4 Replies to “Setting up LEMP stack for local development on Linux

  1. Awesome! Very nice and clean guide to installing and setting up nginx. Seems like configuration files of server blocks are simpler than virtual hosts in Apache. Also, after reading this it looks like nginx is pretty simple to set up and maintain.
    I will definately try setting up my own nginx environment by following this guide!

    Good luck mate with next articles & keep up the great work!

    1. Yeah, nginx is actually simpler to setup than Apache (at least in basic scenarios).

      Thanks for comment, mate!

  2. ***This poem had a unique style of writing and yet it spoke the truth in such a blunt way. This is not a bad thing, for it reached out to the realistic part of the world. The fact that someone always has to go to the root of the problem and fix it as do the people who have witnessed the war to make room for the people who didn’t. “After every war someone has to clean up. Things won’t straighten up themselves, after all.” I like the tone of voice that is being spoken throughout this poem; sincere and intense. Those lines prove the best representation of this emotion.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.