Deploying a Laravel 5.8 Web App on Ubuntu

‘The Right Way’ with SSL, server updates, a firewall and more.

This guide aims to:

Follow industry best practices,

Standardise tooling and configuration steps to minimise ‘wrong-turns’ and incompatibilities amongst different software combinations,

Minimise assumptions regarding prior knowledge,

Ensure best-practice security measures are included,

Use modern tooling such as package managers and git.

If you have suggestions which would further these aims, please let me know!

You will need

A ‘blank’ Ubuntu server (Use the Ubuntu 16 LTS version) (1)

SSH access to that server through a terminal (2)

Your project hosted with git and git installed on your local computer (3)

Once, you are able to login (via ssh) on your new Ubuntu Server, we can move on to deploying!

Any text that is highlighted in a light brown text box like this is a command for entering into your terminal

Step 1: Login as root

Login using your favourite ssh compatible terminal.

ssh root@YourServersIP

If you encounter problems with this step you should consult this tutorial.

Step 1: Automatic Updates

We want to make use of that Long Term Support that our Ubuntu 14 .04 OS supports. As such I recommend installing a program called Unattended Upgrades which allows the server to update with the latest security fixes automatically so we don’t have to monitor those ourselves.

sudo apt-get update

sudo apt-get install -y unattended-upgrades

We can take a look at what unattended updates are enabled through using the nano editor.

sudo nano /etc/apt/apt.conf.d/50unattended-upgrades

Quit the editor by pressing Control + x.

Step 2: Disable Password Login (Optional)

This will make sure that your password isn’t guessed or cracked by a hacker trying to gain entry into the server. This will only allow ssh logins which are more secure.

sudo nano /etc/ssh/sshd_config

Navigate to the line which says PasswordAuthentication. (If it's commented out with a ‘#’ then delete the ‘#’) Make sure it is followed by a ‘no’ rather than a yes. It should appear like this:

PasswordAuthentication no

Reload the ssh service for the changes to take effect by typing this into your console.

sudo service ssh restart

Step 3: Block Hackers with Fail2Ban

Fail2Ban is a program which blocks malicious hackers by using iptables (which have the ability to disallow connections from specific potentially malicious IP addresses).

Fail2Ban detects potential hackers through scouring the servers logs and banning IP addresses whose activity indicates an attack.

Install Fail2Ban.

sudo apt-get install -y fail2ban

Step 4: Necessary Software

Install your Web Server

For 95% of use cases, Nginx will suit your use case.

Install Nginx by entering these commands one by one (If an error is output which is similar to “Unable to resolve host”, try the command without the ‘sudo’ prefixing it).

sudo apt-get install -y software-properties-common

sudo add-apt-repository ppa:nginx/stable

sudo apt-get update

sudo apt-get install -y nginx

sudo service nginx restart

Visit http://YourServersPublicIPAddress and if you don’t see this web page then it didn’t install correctly.

Ignore that it says Debian and not Ubuntu it doesn’t matter. Ubuntu is based on Debian.

Git, Curl, Wget

Let’s install Git for our version control management. Curl and wget are also useful tools for HTTP interactions and are needed to install Composer.

sudo apt-get install -y git curl wget

PHP 7 (Needed for Laravel 5.7)

If you get stuck here consult this website

Firstly, uninstall PHP 5 to make way for PHP 7

sudo apt-get purge php5-fpm

sudo apt-get --purge autoremove

Add the relevant repositories for PHP 7

sudo apt-get install -y software-properties-common

sudo apt-get install -y python-software-properties

sudo add-apt-repository ppa:ondrej/php

sudo apt-get update

Install PHP 7 on the server. The php-fpm extension necessary for Nginx to work with PHP. The other extensions are required for popular frameworks like Laravel.

sudo apt-get install -y php7.2 php7.2-fpm php-mysql php7.2-mysql php-mbstring php-gettext libapache2-mod-php7.2 php-doctrine-dbal php7.2-pgsql php-xml redis-server sudo systemctl restart php7.2-fpm

PHP 5

Run these commands if you don’t want PHP 7 and you want PHP 5 (likely due to its compatibility. Note that Laravel 5.7 required PHP 7).

sudo add-apt-repository -y ppa:ondrej/php5

sudo apt-get update

sudo apt-get install -y php5 php-cli php5-curl

Composer for PHP Dependency Management

curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer

Typing ‘composer’ into terminal should output this

MYSQL for Databases

sudo apt-get install -y mysql-server

Install MYSQL.

sudo mysql_install_db

Alternatively, Install PostgreSQL

sudo apt-get install -y postgresql postgresql-contrib

Node and NPM

Use Node JS? NPM? Yarn? Express? Grunt? Gulp?

curl -sL https://deb.nodesource.com/setup | sudo bash - sudo apt-get install -y npm #Gulp, Grunt and Yarn ln -s /usr/bin/nodejs /usr/bin/node

sudo npm install --global yarn gulp-cli grunt-cli

Ruby

Install Ruby.

sudo apt-get install -y ruby

Python

Ubuntu 14.04 should have Python 2.7 on the server. You can check through this command

python --version

Optional: (Would not recommend unless you know you need Python 3)

I would not suggest this as it can cause incompatibilities with popular programs such as LetsEncrypt.

Remove Python 2.7 and install Python 3 and PIP

sudo rm /usr/bin/python

sudo ln -s /usr/bin/python3 /usr/bin/python

sudo apt-get install -y python3-pip

Step 5: Pulling in your Web App via Git

Navigate to the var/www folder like so:

cd /var/www/

create a new directory for your app

sudo mkdir AppName

go into the created directory

cd yourAppName

Have a production ready Git branch of your App

(For those who don’t have development and production branches already set up.)

When we are developing our app we don’t want to have to make sure all our changes constantly need to be ready for production.

As such, we need to create a production version of our app which we can merge production ready changes into when they are fit for deployment.

On your local machine where you are developing our app (assuming we have installed Git installed) open a terminal and navigate to the folder where you are developing and have your repo. Create a production branch of the project.

//On your local/dev machine!

git branch production

Whenever your development or other branches are ready to have their changes pushed into production we have to enter into the production branch.

So let’s switch into the production branch.

git checkout production

Merge our development changes. Make sure you have committed your changes in the development branch! (Replace master with your development branch name).

git merge --no-ff master

Adding ‘ -- no-ff’ makes sure it creates a new commit is made so we can track development merges into production.

Push this new branch so it appears on our remote repository hosting such as GitHub, GitLab or BitBucket.

git push origin production

(You may want to switch back to your dev branch to develop on it later. Simply use the command ‘git checkout master’ to switch back.)

Pull your production branch down onto your new server

Pull down our production branch code to our server.

Firstly, let’s go to our AppName Folder

cd /Var/www/AppName

Then, init a new git repo

git init

Add your remote git repo from your Git hosting provider (i.e. BitBucket, GitHub, Gitlab, etc)

git remote add origin https://github.com/user/repo.git

Pull in your production branch

git pull origin production

Whenever we need to pull down the production branch of your project just cd into the directory and run ‘git pull origin production’.

Step 6: Configure the Server to serve our website

To configure Nginx open the Nginx config file

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

All our edits are going to be after the first

server {

line and before the closing ‘}’ to make sure the commands are changing the server.

Make your URLs pretty

No one likes to see .html and .php at the end of their URL so get rid of them by going to the Nginx config file.

Change the default ‘location / { }’ block’s contents from

try_files $uri $uri/ =404;

to

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

Disallow access to dot files

Adding this block to your Nginx config will disallow web access to files beginning with ‘.’ which are usually configuration files we don’t ever want users to see such as the ‘.env’ or ‘.git’ file.

location ~ /\.

{

deny all;

}

PHP

Find this line:

index index.html index.html index.nginx-debian.html;

And add index.php, changing it like so:

index index.html index.html index.nginx-debian.html index.php;

If you are going to use PHP 7 copy this block in: (Replace 7.0 with your PHP version such as 7.3)

location ~ \.php$ {

include snippits/fastcgi-php.conf;

fastcgi_pass unis:/run/php/php.7.0-fpm.sock;

}

If you are going to use PHP 5, replace this line in the above block

fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;

with this line and paste the block in (by right clicking).

fastcgi_pass unix:/var/run/php5-fpm.sock;

Edit the Nginx Virtualhosts file to serve the site

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

Change this line

root /var/www/html;

to the folder where your index.php is served from, in laravel its /public

root /var/www/AppName/public;

(excluding comments you have left in) your file should look similar to this

Exit editing the nginx config through Control+X.

Running the terminal command ‘nginx’ will let you know if the config is valid.

Restart Nginx

You’ve made changes to the config, let’s restart Nginx.

sudo service nginx restart

Setting Permissions

Set the permissions for the public directory (where the html/css/js) files from your site are to be served from. These commands are Laravel specific.

sudo chown -R www-data:www-data /var/www/AppName/public sudo chmod 755 /var/www sudo chmod -R 755 /var/www/AppName/bootstrap/cache

sudo chmod -R 755 /var/www/AppName/storage

Step 7: Installing Dependencies

Install PHP dependencies via Composer

composer install

Install JS dependencies via NPM

You may skip this step if your JS is already compiled for production and was included in your production branch which we brought in via version control (git).

I’d generally suggest building your JS frontend dependencies locally (with the correct production settings then uploading to version control and deploying. However, if you insist on doing it on the server then run these commands accordingly)

npm install

npm run production //Your command to compile frontend javascript

Step 8: Setting up your Database

Create a Database for your Web Application

Mysql DB Setup

L create a mysql database:

mysql -u root -p CREATE DATABASE appname_db;



Create a user for that database: (first command is one line)

GRANT ALL PRIVILEGES ON app_db.* TO 'appname_user'@'localhost' IDENTIFIED BY 'password-of-your-choosing'; flush privileges;

Exit the DB

exit

Postgresql DB Setup:

Create a global postgres user:

sudo su - postgres

psql

Create a database for your app:

CREATE DATABASE appname_db;

Create a user for your app’s database:

CREATE USER appname_user WITH PASSWORD 'password_of_your_choosing';

Allow our user access to the DB

GRANT ALL PRIVILEGES ON DATABASE appname_db TO appname_user;

Exit the DB configuration

\q

exit

Set Up Your Environment

Create an environment file for your Laravel web app.

cd /var/www/AppName

cp .env.example .env

nano .env

Change the necessary production environment variables.

Change:

APP_ENV=local

APP_DEBUG=true

to

APP_ENV=production

APP_DEBUG=false

Generate an application key to be used for encryption

php artisan key:generate

Connect your Database

In your ENV, change the Database Name/Username/Password options to what we previously created.

Then migrate your database, in Laravel it’s done like so:

php artisan migrate:install

php artisan migrate

Step 9: Configure LetsEncrypt for SSL

LetsEncrypt is a great free SSL provider. We are going to use CertBot to install it on our server.

Run these commands to download Certbot:

sudo apt-get install -y software-properties-common

sudo add-apt-repository ppa:certbot/certbot

sudo apt-get update

sudo apt-get install -y python-certbot-nginx

Set up the website’s virtualhost

Edit the existing virtualhost

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

Change (Replace example.com with your domain name)

server_name _;

to

server_name example.com www.example.com;

Change

listen 80 default_server; listen [::]:80 default_server;

to

listen 80 default_server; listen [::]:80;

Copy over the new virtualhost into its own file and then into sites-enabled:

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/

Install your SSL certificate provided by LetsEncrypt

sudo certbot --nginx

When it asks you if you wish to have SSL for both example.com and www.example.com simply write 1,2 for both.

Voila!

Step 10: Check https://example.com to see your website in all its glory

If you have any problems please reach out to me on Medium.

I hope this helped you out, servers can be really quite hard to get your head around. Please let me know if you can think of any improvements or have any other feedback!