You don’t need a team of Dev-Op professionals to deploy and maintain your Web App!
This guide aims to:
- Follow industry best practices,
- Standardise tooling configuration steps to minimise ‘wrong-turns’ and incompatibilities among different software,
- Minimise assumptions regarding prior knowledge (Such as using nano),
- Assure standard security measures are configured such as auto-updates and Fail2Ban protection,
- Accomodate modern dev tooling such as package managers and git.
Written for all deploying web apps in popular languages such as Python, Ruby, Nodejs. However, it’s especially applicable for deploying PHP apps based on the Laravel Framework.
- 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)
- This likely would be using a service such as AWS EC2 or DigitalOcean (DO). I would recommend DigitalOcean(DO) as a server considering their price and ease of use, here is a tutorial on how to spin up a new server on DO.
- Here is a DO tutorial on how to set up SSH access. SSH is used to connect to the server directly and be able to run commands as well as transfer files via the protocol. We don’t use ftp, it’s not secure.
- Here is a guide to installing git. Here is a guide to setting up your project as a git project if it’s not already. For free private repositories I suggest Bitbucket.
Once, you are able to login (via ssh) on your new Ubuntu , we can move on to deploying!
Any text that is highlighted in a light brown text box like this is a command you should enter into your terminal
Using your favourite ssh compatible terminal. Login like so:
ssh root@YourServersIP
If you have any problems with this step you should consult this tutorial.
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 be updates with the latest security fixes automatically so we don’t have to monitor it ourselves.
sudo apt-get update
sudo apt-get install -y unattended-upgrades
We can take a look at what unattended updates are enabled through taking a look in nano
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
I recommend that only (as should appear by default) the line with security in it should not have a ‘//’ like so to maintain compatibility if a breaking change is included in an update.
Unattended-Upgrade::Allowed-Origins {
“${distro_id}:${distro_codename}-security”;
// “${distro_id}:${distro_codename}-updates”;
// “${distro_id}:${distro_codename}-proposed”;
// “${distro_id}:${distro_codename}-backports”;
};
Now quit the editor by pressing Control + x.
Here only the security upgrades will be automatically installed. This improves security while preventing your web apps breaking.
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 .
sudo nano /etc/ssh/sshd_config
then navigate the the line which says PasswordAuthentication. (If its commented out with a ‘#’ then delete the ‘#’) Make sure it is followed by a ‘no’ rather than a yes. As such it should appear like this:
PasswordAuthentication no
Now quit the editor by pressing Control + x (and save by answering the prompt with ‘y’). Now reload the ssh service for the changes to take effect by typing this into your console.
sudo service ssh restart
Fail2Ban is a program which blocks malicious hackers by using iptables (which have the ability of disallowing connections from specific IP addresses).
Fail2Ban detects potential hackers through scouring the servers logs and banning IP addresses that indicate attacks such as IPs trying to login multiple times but failing or trying random queries in your web software (i.e. your PHP page) seeking to find exploits.
Throw this command into terminal to install the program
sudo apt-get install -y fail2ban
Step : Install your Web Server
Here you have two choices; Apache or Nginx. For 95% of use cases, Nginx will be sufficient. And I mean that. If its a Web App/Site/ API Nginx is perfect and considering its faster than Apache its recently become more commonly suggested.
Now 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
Now visit http://YourServersPublicIPAddress and if you don’t see this web page then it didn’t install correctly and go redo the installation steps.
Ignore that it says Debian and not Ubuntu it doesn’t matter. Ubuntu and Debian are just based off of the same Linux system.
Most of us will need Git on the server for deployments. Curl and wget are also useful tools for HTTP interactions and are needed to install Composer. Lets install them all.
sudo apt-get install -y git curl wget
First we will get rid of php5 if its on the machine
sudo apt-get purge php5-fpm
sudo apt-get --purge autoremove
First we will have to add the relevant repositories
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
Now lets put PHP 7 on the machine; the php-fpm extension necessary for Nginx to work with it, extensions required for popular frameworks like Larave, and the mysql extension necessary for working with mysql.
sudo apt-get install -y php7.0 php7.0-fpm php-mysql php7.0-mysql php-mbstring php-gettext libapache2-mod-php7.0 php-doctrine-dbal php7.0-pgsql php-xml redis-server
Apply sensible security defaults (These are one line commands)
sudo -- sh -c "echo 'cgi.fix_pathinfo=0' >> /etc/php/7.0/fpm/php.ini"
sudo -- sh -c "echo 'cgi.fix_pathinfo=0' >> /etc/php/7.0/cli/php.ini"
sudo systemctl restart php7.0-fpm
If you don’t want PHP 7 and you want PHP 5 (likely due to its compatibility)
sudo add-apt-repository -y ppa:ondrej/php5
sudo apt-get update
sudo apt-get install -y php5 php-cli php5-curl
curl -sS
| sudo php -- --install-dir=/usr/local/bin --filename=composer
Now type ‘composer’ into terminal and it should output this
sudo apt-get install -y mysql-server
now allow MYSQL’s use
sudo mysql_install_db
sudo apt-get install -y postgresql postgresql-contrib
Use Node JS? NPM? Yarn? Express? Grunt? Gulp?
curl -sL https:
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
Install Ruby
sudo apt-get install -y ruby
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 ‘lets encrypt’.
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
Navigate to the var/www folder like so
cd /var/www/
create a new directory for your app (No spaces please. Makes life easier)
sudo mkdir AppName
go into the created directory
cd yourAppName
- For beginners who don’t have development and production branched 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 ready (such as a new feature).
**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. Now create a production branch of the project. If you are already developing on a ‘development’ branch you know why you can skip this step.
//On your local/dev machine!
git branch production
Whenever your development or other branches are ready to have their changes into production we have to enter into the production branch.
So lets switch into the production branch. We use the ‘git checkout’ command to enter and exit branches. Take a look at all branches available using the ‘git branch -a’ command.
git checkout production
And merge our development changes (after you have made sure to commit your changes in the development branch!) (replace master with your dev branch)
git merge --no-ff master
Adding ‘ -- no-ff’ makes sure it creates a new commit so we can track development merges into production.
Now lets 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 in case you forget later you are on production. Simple use the command ‘git checkout master’ to switch back.)
Now on our new server in the AppName directory lets pull down our our production branch into our server
First lets go into our AppName Folder
cd /Var/www/AppName
Then, init a new git repo
git init
Now add your remote git repo on BitBucket, GitHub, whatever
git remote add origin
now pull in your production branch
git pull origin production
Great, now whenever we need to pull down the production branch of your project just cd into the directory and run ‘git pull origin production’.
We need to configure nginx to appropriately serve the directory.
This can be a bit finicky so pay close attention here as one out-of-place semi-colon could mean your website cannot be served/accessed.
To configure Nginx (Which we will need to) open the Nginx config like so
sudo nano /etc/nginx/sites-available/default
now 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 and not flying out in no mans land where nothing happens.
No one likes to see .html and .php at the end of their web page so lets get rid of them by going to the Nginx config file.
**Change **the default ‘location / { }’ block’s contents from
to
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 ‘.git’ file. Lets disable access to those files like so
location ~ /\.
{
deny all;
}
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:
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;
(I would suggest PHP 5 if PHP is not going to be the base of your main application as its has increased compatibility over PHP7. Otherwise I would choose PHP7 if PHP is your main platform as its faster.)
sudo nano /etc/nginx/sites-available/
now change this line
root /var/www/html;
to the folder where your index.php is served from, in laravel its /public
root /var/www/
/public;
Exit editing the nginx config through Control+X then typing Y to save the changes then enter again.
Check the Nginx config is valid
Running the command ‘nginx’ will let you know if the config is valid, if it
(excluding comments you have left in) your file should look similar to this
Now you’ve made changes to the config, lets restart Nginx
sudo service nginx restart
First of all set the permissions for the public directory (where the html/css/js files for your site are to be served from.
These folders are applicable to Laravel applications, check your framework’s documentation for it’s respective permissions.
sudo chown -R www-data:www-data /var/www/
/
sudo chmod 755 /var/www
sudo chown -R 777 /var/www/
/bootstrap/cache
sudo chown -R 777 /var/www/
/storage
composer install
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 //or whatever your command is to compile & minify
We now need to set up a DB for your App. If your app uses Postgresql then skip the mysql setup and visa-versa for those using mysql.
First lets create a mysql database: (remember the password you input when prompted)
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:
(replace appname_user, appname_db)
GRANT ALL PRIVILEGES ON DATABASE appname_db TO appname_user;
Exit the DB configuration:
\q
exit
Create an environment file for your app if it requires one. Laravel does.
cd /var/www/
sudo nano .env
In the nano editor copy your dependencies from your dev .env and change what is necessary for the production instance of your app.
i.e. Change:
APP_ENV=local
APP_DEBUG=true
to
APP_ENV=production
APP_DEBUG=false
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
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)
to
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/
Now run this to install your SSL:
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.
Step 10: Check https://example.com to see your website in all its SSL glory
If you have any problems please reach out to me here:
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!