× {{alert.msg}} Never ask again
Receive New Tutorials
GET IT FREE

DevOps Tutorial: Setting up a Modern Web Stack on Ubuntu

– {{showDate(postTime)}}

This tutorial will teach beginners to back-end development how to set up a modern web stack on Ubuntu.


The “LERPP” stack

It is quite possible that you haven’t heard the name “LERPP” before. That is actually because… I coined it.

LERPP stands for Linux, (E)nginx, Redis, PostgreSQL and PHP. It is a complete back-end solution for all your web needs!

In this article, we will set up the stack, and create an empty playground with the necessary assets for you to experiment, learn, and build! This setup is production ready.

The Linux distribution we will use is Ubuntu 14.04 LTS release, x64 version. You can follow the same steps on pretty much any Debian based operating system.

Basic knowledge about the Operating System and the terminal is required to complete this tutorial properly. A good read for this would be “Using The Terminal” article from the Ubuntu Community Wiki.

Why use these technologies only?

Some of you might be familiar with the LAMP stack. LERPP can be considered the modern version of LAMP.

Linux is already well known for its popularity for running servers. There is no denying it. And PHP has been the de-facto web language for a long while as well… but Apache and MySQL are losing it. There are better alternatives around now.

Nginx is gradually replacing Apache because of its asynchronous model and high performance on concurrent connections.

PostgreSQL was once believed to be non user friendly, and slow in comparison to MySQL. Ever since PostgreSQL 8.4 released, things have changed a lot.

PostgreSQL is strongly ANSI SQL compliant, has better support for data integrity. It is also very much better than MySQL at handling a lot of connections at the same time (concurrency). And it is as much fast as other database engines like MySQL, if not faster. And with the recent release of PostgreSQL 9.4 with JSONB (Binary JSON) support, PostgreSQL can even take the place of popular No-SQL database engines like MongoDB etc.

Redis has now overpowered memcache(d). With the release of Redis 3.0 RC, native cluster support is coming. If you need a caching solution, you need Redis.

As for PHP, the language which everyone loves to hate, it is now getting really better. After the release of PHP 5.5, development has improved a lot. Better support for namespaces, the awesome dependency manager Composer, an inbuilt development server, generators, interactive CLI and some other changes, PHP is back on track. Not to mention, it already has the advantage of having a large community.

With all the new improvements, it is time to acknowledge the efforts of the PHP team, and give the language the praise it deserves.

Ubuntu 14.04 LTS

I am assuming you already have Ubuntu installed. If not, you can download a fresh ISO from Ubuntu official download page, and install the OS via a USB drive.

After that, run

sudo apt-get update

to update the software sources on your system.

It is recommend to use the latest LTS releases only, because they are supported for longer time periods, and are more stable.

The server: nginx

Nginx is available under the launchpad ppa:nginx/stable. Go ahead and add the repository via the following command, and refresh the software sources.

sudo add-apt-repository ppa:nginx/stable
sudo apt-get update

Now we can issue the install command,

sudo apt-get install nginx

This will install nginx stable on your system. To verify this, type your server’s IP Address in the browser, and you should see the nginx welcome file!

If you are running the server for local development only, you can also use http://127.0.0.1 for the IP address.

Database: PostgreSQL

PostgreSQL version 9.3 is present in the official Ubuntu repositories already. Just type the following in your terminal, and you should be good to go.

sudo apt-get install postgresql-9.3

But! The more recent release, version 9.4 is not yet available in the Ubuntu Trusty repositories. To solve this, you can add the PostgreSQL APT repository for the latest version.

sudo deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main

wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
  sudo apt-key add -

sudo apt-get update
sudo apt-get install postgresql-9.4

All users are advised to install the latest version only.

At this point, some people might advise you to also install the graphical utility pgAdmin, but I would go ahead and take the bold move to say that the command line is usable and sane enough. A developer shouldn’t find it difficult to type the specific thing he wants to get done, rather than scrolling through a dozen of icons trying to identify the one he needs.

Configuring PostgreSQL

Users accustomed to the *AMP stacks and auto-installers might find this part difficult, but it is not.

PostgreSQL will create a user-group on Ubuntu, by the name postgres. Using this user directly in your application is hardly a good idea, because it is a superuser account. We need to create a user, and a database. We also need to ensure that the new user is able to login, has a password, and has privileges to access only this new database.

PostgreSQL has the concept of roles, instead of users and usergroups. A role can be a user, and a usergroup as well. This is actually a very powerful thing, and gives you a lot more flexibility. I recommend reading more about this at the official documentation.

The way PostgreSQL manages users, is that it maps the usernames from your system, straightway to PostgreSQL. What this means is that if your system username is awal, then there needs to be a PostgreSQL user with the same name for the user awal. But as I said before, PostgreSQL comes with the postgres user only…

So we need to login to the default postgres user account that PostgreSQL has created, and create the database and new role from there.

sudo -u postgres psql postgres

This would log you into the postgres account. It is recommended to change the password for this account via the following command.

\password postgres

You would be prompted to enter the new password, and then confirm it.

Now you can create a new database, via the traditional SQL commands.

CREATE DATABASE <database-name>;

We will also create a new role with limited privileges, which allows us to operate on the new database.

CREATE ROLE <username> LOGIN PASSWORD '<password-in-quotes>';

This will create a new role under the given username, who can login, via the password you enter. The password MUST be written between single quotes.

Further, your username MUST be the same as your computer’s username. This is because of the way in which PostgreSQL manages users, described previously.

Now we need to provide the new user with the privileges to operate on the new database, and we are good to go!

GRANT ALL PRIVILEGES ON DATABASE <database-name> TO <username>;

And with this, you have successfully configured PostgreSQL, created a database, and a user for your app!


Cache: Redis

Installing Redis is very simple. The Ubuntu Trusty repositories already have ~latest version.

sudo apt-get install redis-server

After this, you can play with Redis from the command line by launching the redis-cli program from your terminal.

awal@awalGarg:~$ redis-cli
127.0.0.1:6379> ping
PONG

If you are new to Redis, checkout the Redis Interactive Tutorial! Redis is very simple to learn, yet very powerful. If there are two words that won’t fit to describe Redis, they are “hard” and “slow”!


The Language: PHP

The server software (nginx) receives all incoming requests, but it can’t process the scripts (PHP) stored on the server. What we do is use a “Common Gateway Interface”. The Gateway routes requests from nginx to the PHP engine which process those scripts. The gateway interface nginx uses is called FastCGI. So we just need PHP to listen to the requests that FastCGI would route.

We can use PHP’s FastCGI process manager, aka php-fpm, which will listen to those requests, parse it with the PHP engine, and send the result back to nginx via the gateway, so that nginx can send it back to the origin of the request (i.e. the user).

Phew! That seems like a lot of work, eh? No worries!

Let us first install the PHP-fpm package.

sudo apt-get install php5-fpm

APT will automatically install the php5-common package, which allows parsing the PHP scripts. At this point, you can go straightway to setting up nginx to route requests via FastCGI to php5-fpm, but I highly recommend installing two more package that helps in development – php5-cli and php5-xdebug.

The CLI package (Command Line Interface) allows you to use PHP from the command line, which is very helpful for testing simple stuff. It also installs the php5-readline package, which brings to you an interactive console for PHP. You can even use this to run a development server from within PHP, which is very powerful for development! It gives notices, warnings, and errors in real-time, and other functionality missing in production ready server engines like nginx. And XDebug is a well known package which allows powerful debugging for PHP scripts.

sudo apt-get install php5-cli php5-xdebug

With this we are done. You can test the PHP command line interactive console via running php -a.

awal@awalGarg:~$ php -a
Interactive mode enabled

php > var_dump('Hello World!');
string(12) "Hello World!"
php >

Stitching pieces together

We have installed stuff, now we need to make them work together. Let us start with installing the PostgreSQL drivers for PHP, which will allow PHP to interact with PostgreSQL.

sudo apt-get install php5-pgsql

You can now use the following to test the availability of the PostgreSQL PDO driver.

php -r 'var_dump(PDO::getAvailableDrivers());'
array(1) {
  [0] =>
  string(5) "pgsql"
}

Let us now setup the nginx configuration to route requests via FastCGI to PHP-FPM. Although I will try to demonstrate the common basics here, it is highly recommended that you read and learn about the nginx configuration, specially the pitfalls.

The nginx configuration is divided mostly in two parts. One is the global config which is included by default on installation of nginx at /etc/nginx/nginx.conf. The default configuration file would automatically include sub-config files from the directory /etc/nginx/sites-enabled. So you have to store your config files in the sites-enabled directory only, and they should have the extension .conf.

By convention, each config file in the sites-enabled directory would contain the config for one website. Here is how a simple configuration file looks.

server {
    listen 80 default_server;

    root /usr/share/nginx/html;

    index index.php index.html index.htm;

    server_name localhost;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

It is not actually difficult to read. Let us take each part, and understand what it does.

listen 80 default_server;

The listen keyword makes nginx listen to the port passed (80, in this case), and it will be the default_server.

root /usr/share/nginx/html;

The root keyword defines where are the scripts for your website placed. So all the files you store in /usr/share/nginx/html could be accessible from outside.

index index.php index.html index.htm;

The index keyword defines the main entry point of your website. So if your website is http://example.com, any user going to the website directly would be served the first file present in the root directory passed to the index keyword. So if someone goes to http://example.com, nginx would try to find index.php first. If the file is present, it would be served, else, it would try to match the next one, and so on. This way, your users can type http://example.com/ instead of http://example.com/index.php.

server_name localhost;

The server_name keyword defines the exact name for the website. localhost is used for development purposes. It is not accessible from other computers. For public websites, replace localhost with your site’s url.

Note that you can’t just define a name here and expect it to work for everyone. You have to register that domain name, and configure the DNS. Your ISP would provide the information required for this.

location / {
    try_files $uri $uri/ =404
}

The location keyword defines what the user will be served for the given path. In this case, the path is /, which refers to the root directory (yes, it covers sub-directories as well).

The try_files keyword would try to find files in the given order, and serve the one that is found first. The $uri variable is available to the nginx configurations, and holds the URI (and not the URL).

So if the user requests http://example.com/foo, $uri would contain /foo, and nginx would thus try to find the file foo in the root directory. If it is found, it would be served.

If it is not found, nginx would try to find $uri/ which means /foo/, which means the directory foo present in the root directory. If the directory exists, the request would be served from that directory.

If the directory is not found as well, nginx would return a 404 status code, which means the requested resource was not found.

location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
}

This might look scary at first, but is not that difficult to understand.

The ~ symbol in after the location keyword means that the path given is in the form of a regex. The regex we gave is \.php$, which matches resources which have .php at the end of the resource name.

Since PHP files can’t be directories (obviously), we do not specify the $uri/ word in the try_files directive in this case.

Next, we specify the parameters for the FastCGI interface. fastcgi_pass contains the reference to the listener to which the request must be passed.

fastcgi_index is similar to the index directive which we described earlier, fastcgi_param SCRIPTNAME passes the given parameter under the name SCRIPTNAME to the FastCGI interface.

include fastcgi_params directive will include the file named fastcgi_params already present in the nginx config directory. It contains some conventional parameters like $remote_addr etc. passed to PHP, which are then available to your PHP scripts under the $_SERVER super-global variable.

So go ahead and save this file under /etc/nginx/sites-enabled under the name server.conf, and restart nginx via sudo service nginx restart. Then create a file index.php in /usr/share/nginx/html with the following contents:

<?php
phpinfo();

Save the file, and navigate to http://localhost (or your site’s name), and you should see the PHP info page!

The only thing which now remains is using Redis with PHP. Using Redis with PHP would require you to use a Redis Client library for PHP. You can use anyone listed at the Redis Clients page under the PHP section. Installation and usage would vary depending upon the client you chose.


Conclusion

So that was it… wasn’t much difficult, right? In the end, we managed to assemble the following structure:

backend structure : cc-by-sa-3.0backend structure : cc-by-sa-3.0

The aim of this article was not just to help you get the thing working, but also to introduce you to some of the things which happen behind the scenes when “auto-installers” run. The reason why knowing them is beneficial, is that it gives you more flexibility. You can configure your environment in the way you find comfortable.

Further, it allows you to use the latest tech available. Most auto-installers are one or two versions behind the latest versions of the packages used, but not so when you install them manually!

Lastly, some in-depth knowledge is always beneficial. A lot of bugs that you might come across while programming would be related to the setup you are using. If you had used an auto-installer without knowing what the installer did, you just won’t be able to fix it (And this happens a lot, specially to beginners). Not to mention, there are hardly any auto-installers which can be used reliably in production.

You also noticed that I discouraged using the graphical utility for PostgreSQL and pressed on the use of the Command Line Interface. The reason was that if you are using an SQL based database engine, you should be aware of the commands used to manage the database. Using the command line is a good practice for that, and above all, you know what exactly you are asking the database engine to do. GUI’s often tend to cover up some of the lesser known facts about the usage of specific commands, which makes those commands even less known. This makes debugging of some edge cases almost impossible.

Other than that, if you know ‘what’ the GUI is doing, and you feel confident about your skills even without the GUI, then go ahead and use it if it saves your time. But ignoring the command line is simply not the solution, at-least not for the long run.

We also covered a brief about the nginx configuration, how PostgreSQL manages users, and how nginx and PHP actually interact (via a CGI). I hope you found the article useful. Good luck!


Codementor Awal Garg is a web developer fluent in HTML/CSS and JavaScript for the frontend, and also uses JavaScript at the backend with node and express.

Awal GargNeed Awal’s help? Book a 1-on-1 session!

View Awal’s Profile or join us as an expert mentor!



Author
Removed User
Removed User
4.7
Hire the Author

Questions about this tutorial?  Get Live 1:1 help from DevOps experts!
Benjamin Kappel
Benjamin Kappel
5.0
Experienced Blazor, .NET Core developer (5+ years) and coding teacher
I'm a NET Core developer to the bones. My co-workers always describe me as a having integrity, reliable person and I am able to create a trustful...
Hire this Expert
Anuvrat Parashar
Anuvrat Parashar
5.0
Engineer turned entrepreneur debugging software for 15 years.
I succeed or your **money back.** Skillset includes: python, elixir, rust, golang, c++, lua et al. Diving into, understanding and debugging...
Hire this Expert
comments powered by Disqus