Ubuntu 16.04 LTS setup for AWS EC2 micro instance. The fast way. (LEMP Stack)

I've recently grown tired of using Amazon Linux AMI for my servers as you can never quite replicate the same development environment.

So.. here we are!
LEMP stack for the Ubuntu 16.04 LTS with PHP 7. Woot!

If you need to start up a new instance, take a look at my previous post. The only difference there would be to select Ubuntu OS instead of Amazon Linux AMI.
I'm assuming you're already ssh-ed into your instance, so.. Let's go!

Welcome Screen
As a welcome screen you see this unsettling message:

WARNING! Your environment specifies an invalid locale.  
 The unknown environment variables are:
 This can affect your user experience significantly, including the
 ability to manage packages. You may install the locales by running:

   sudo apt-get install language-pack-en
   sudo locale-gen en_CA.UTF-8

To see all available language packs, run:  
   apt-cache search "^language-pack-[a-z][a-z]$"
To disable this message for all users, run:  
   sudo touch /var/lib/cloud/instance/locale-check.skip

Don't panic! Just type $ sudo apt-get install language-pack-en to install the missing locale files.

First, let's make sure you have all the latest goodness.
$ sudo apt-get update
$ sudo apt-get upgrade

This process will take a minute or two. If you want to see what's gonna be upgraded, you can run $ sudo apt-get list --upgradable at any time to get a list.

We only have 1 GB of memory available and no virtual memory to extend it. That alone is a major cause of server crashes and hangups. Simply creating a swap file is a well-known, effective micro instance tweak.
Create and activate a 2 GB swap file with the following:
$ sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
$ sudo mkswap /swapfile
$ sudo chmod 0600 /swapfile
$ sudo swapon /swapfile
We generate the file with dd, make a swap filesystem with mkswap, set proper permissions with chmod, and mount it with swapon.

Our system is using the swap file for this session. To mount the volume at boot, we need to modify a system file. You can do this by typing:
$ sudo sh -c 'echo "/swapfile none swap sw 0 0" >> /etc/fstab'

Now that we have virtual memory, let's give it a double check.
Type $ free and you should see the following.
You can see the last line, with our 2Gb of swap file!

sudo atp install build-essential libssl-dev
sudo apt install nginx php-cli php-fpm php-mysqlnd php-mcrypt php-mbstring php-curl php-xdebug phpunit

Let's enable the module we just installed.
sudo phpenmod mcrypt
sudo phpenmod mbstring
sudo phpenmod curl

Before we can test Nginx, we need to configure our firewall to allow access to the service. You can check the configuration of the firewall at anytime typing $ sudo ufw app list. Pretty awesome!

Open up only the ports that you will need.
$ sudo ufw allow 'Nginx HTTP' — Port 80.
$ sudo ufw allow 'Nginx HTTPS' — Port 443.

Because we're running Nginx in a micro instance we need to look out for resources usage. Let's put in a few parameters to optimize it.
$ sudo vim /etc/php/7.0/fpm/php-fpm.conf

Paste these few lines at the end of the file:

emergency_restart_threshold = 10  
emergency_restart_interval = 1m  
process_control_timeout = 10s

pm.max_children = 20  
pm.start_servers = 5  
pm.min_spare_servers = 5  
pm.max_spare_servers = 20  
pm.max_requests = 200

php_admin_value[memory_limit] = 64M  

We need to make a slight configuration change to make our setup more secure. In the main php-fpm configuration file, the standard setting for cgi.fix_pathinfo is set to 1. This is extremely insecure because it tells PHP to attempt to execute the closest file it can find if the requested PHP file cannot be found. This would allow users to craft PHP requests in a way that would allow them to execute scripts that they shouldn't be allowed to execute.

To rectify that, type:
$ sudo sed -i "s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/" /etc/php/7.0/fpm/php.ini.

Another best practice I suggest to remove servers tokens. This will prevent Nginx from outputting information about your current installation, version numbers, etc.
To achieve this:
$ sudo vim /etc/nginx/nginx.conf
Uncomment server_tokens off; and save.

Nginx by default is setup to work with the group www-data. We need to set the proper permission schema so that our www-data user will have write permissions without owning the files.
Do do this we can type:
$ sudo usermod -a -G www-data ubuntu
$ exit - this will terminate your ssh session. Ssh in again and type, $ groups to verify that the new www-data group has been added to your the ubuntu user.

It's time to set our default permissions on the /var/www folder where we will put our projects.
Set write permissions to /var/www for www-data group.
$ sudo chown -R www-data:www-data /var/www

We need to change the web root directory permissions so that the www group has write permissions:
$ sudo chmod 2775 /var/www
We also need to set the same permissions for any future subdirectories:
$ find /var/www -type d -exec sudo chmod 2775 {} +
Then we do the same for files within those directories and subdirectories:
$ find /var/www -type f -exec sudo chmod 0664 {} +

Now we're good to configure our website and the location where your project will be. Feel free to customize this part with your own domain and project name.
$ sudo mkdir /etc/nginx/sites-enabled
$ sudo vim /etc/nginx/sites-enabled/example.com

server {  
    listen 80;
    server_name example.com;

    root /var/www/example/public;
    index index.html index.htm index.php;

    charset utf-8;

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

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log /var/log/nginx/example.com_access.log;
    error_log /var/log/nginx/example.com_error.log;

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    location ~ /\.ht {
        deny all;

server {  
    listen 80;
    server_name www.example.com;

    return 301 http://example.com$request_uri;

Before we start our services, we need to create our project folder.
$ sudo mkdir -p /var/www/example

To test things out, let's put a piece of php in it.
$ cd /var/www/example
$ sudo mkdir public
$ cd public
$ sudo vim index.php

Copy and paste the following.

  echo 'Hello Human!';

Save the file and exit.

Let's restart Nginx to apply the changes we just made.
$ sudo systemctl restart nginx

If you want to run a quick test, you can type in http://your-public-ip-address in your browser and you should see:
Take a moment, to congratulate yourself!

Install MySql server with:
$ sudo apt-get install mysql-server

To secure your database installation type:
$ sudo mysql_secure_installation Set a root password and accept the remaining steps.
Consider your need for remote access. If you need it, say no when asked: "Disallow root login remotely?".

Enjoy your new server!