Wednesday, 25 October, 2017 UTC


Summary

In my last post I showed how to deploy AdonisJS apps to Heroku. In this tutorial, I'll be showing you how to deploy your AdonisJS application to Digital Ocean. We'll also be deploying the task list app we built earlier on.
Prerequisites
This tutorial assumes the following:
  • Digital Ocean account (obviously).
  • SSH set up on your local computer, you might want to follow https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-digitalocean-droplets for a walk through.
Create Droplet
Login to your Digital Ocean account and create a new droplet (server). We’ll be going to with One-click apps. Select NodeJS as shown below:
Next, we’ll choose the $10 plan. Though the task list app will work perfectly on the $5 plan, but we won’t be able to install the NPM dependencies because the NPM requires at least 1GB RAM for installing dependencies. Though there is a way around this by creating swap memory which is beyond the scope of this tutorial.
Next, select a datacenter region, we’ll go with the default:
Next, add your SSH key. If you have already added SSH keys to Digital Ocean before, you can choose from those:
If not you will need to click on the New SSH Key button to add a new SSH key. You can get your SSH key by running the command below on your local computer:
cat ~/.ssh/id_rsa.pub
The command above will print your SSH key on the terminal, which you can then copy and paste in the SSH Key Content field. Also, give your SSH key a name.
Finally, choose a hostname for the droplet and click the Create button.
After some couple of seconds, you’ll have your new server up and running on Ubuntu 16.04 and NodeJS version 6.11.2.
Also, note the IP address of the server as we’ll be using it to access the server.
Create Non-root User
Before we start configuring the server for the task app, let’s quickly create a non-root user which we’ll use henceforth for the rest of the tutorial.
Tip: As a security measure, it is recommended to carry out tasks on your server as a non-root user with administrative privileges.
First, we need to login to the server as root. We can do that using the server’s IP address:
ssh [email protected]_ADDRESS
Once we are logged in to the server, we can move on to create a new user:
adduser mezie
This will create a new user called mezie, you can name the user whatever you like. You will be asked a few questions, starting with the account password.
Having created the new user, we need to give it administrative privileges. That is, the user will be able to carry out administrative tasks by using sudo command.
usermod -aG sudo mezie
The command above adds the user mezie to sudo group.
Now the user can run commands with superuser privileges.
Setup SSH Key For The New User
You need to copy your public key to your new server. Enter the command below on your local computer:
cat ~/.ssh/id_rsa.pub
This will print your SSH key to the terminal, which you can then copy.
For the new user to login to the server with SSH key, we must add the public key to a special file in the user's home directory.
Still logged in as root on the server, enter the following command:
su - mezie
This will temporarily switch to the new user. Now you’ll be in your new user's home directory.
Next, we need to create a new directory called .ssh and restrict its permission:
mkdir ~/.ssh
chmod 700 ~/.ssh
Next, within the .ssh directory, create a new file called authorized_keys:
touch ~/.ssh/authorized_keys
Next, open the file with vim:
vim ~/.ssh/authorized_keys
Next, paste your public key (copied above) into the file. To save the file, hit esc to stop editing, then :wq and press ENTER.
Next, restrict the permissions of the authorized_keys file with this command:
chmod 600 ~/.ssh/authorized_keys
Type the command below to return to the root user:
exit
Now your public key is installed, and you can use SSH keys to log in as your user.
To make sure you can login as the new user with SSH. Enter the command below in a new terminal on your local computer:
ssh [email protected]_ADDRESS
If all went well, you’ll be logged in to the server as the new user with SSH.
The rest of the tutorial assumes you are logged in to the server with the new user created (mezie in my case).
Updating to Latest NodeJS Version
Notice the server currently have NodeJS version 6.11.2, but AdonisJS v4.0 requires NodeJS v8.0 or greater. So we need to upgrade it to the latest version. We’ll do that using a NPM package called n. This will allow us to install multiple versions for NodeJS which we can easily choose from as the case may be. Enter the commands below on the server:
// install n package globally
npm install -g n

// install latest version of Node.js which v8.6.0 as at this tutorial
n latest
If we check NodeJS version we’ll still see v6.11.2. Though we already installed the latest version, for the this to take effect enter the command below:
bash
This is will simply start another bash instance. It’s more like restarting bash.
Install Nginx
We’ll install Nginx as the webserver to be used for reverse proxy which will allow us to access the app directly with an IP address or domain instead of tacking port to the IP address. Eg. 102.123.83.29:5000.
sudo apt-get update
sudo apt-get install nginx
Because we chose One-click apps while creating our droplet, ufw firewall is setup for us and running. Now, we need to open firewall for only HTTP since we are not concerned with SSL in this tutorial:
sudo ufw allow 'Nginx HTTP'
Install MySQL
Next, we need to install MySQL and set up database and user to be used by the task list app.
sudo apt-get install mysql-server
Enter a root password when prompted. Next, we'll finish configuring MySQL:
sudo mysql_secure_installation
Enter the necessary options when prompted.
With the MySQL setup, we need to create a database and a user.
First, login to the MyQSL server:
mysql -u root -p
Provide the root password you enter above while installing MySQL. Once you are logged in, create a new database:
CREATE DATABASE adonis_tasks;
Also, create a new user:
CREATE USER 'mezie'@'localhost' IDENTIFIED BY 'password';
Replace mezie and password with your user and password respectively.
Next, we need to grant the new user priviledges to the tables on the new database:
GRANT ALL ON adonis_tasks.* TO 'mezie'@'localhost';
For the changes to take effect, run:
FLUSH PRIVILEGES;
With the initial setups out of the way, let’s now focus on the app itself. We are going to clone the app unto the server directly in the user's home directory (that is, /home/mezie in my case):
git clone https://github.com/ammezie/adonis-tasks.git
Next, we install the dependencies:
cd adonis-tasks
npm install --production
Next, create .env file:
vim .env
And paste the following into it:
// .env

HOST=127.0.0.1
PORT=3333
NODE_ENV=production
CACHE_VIEWS=true
APP_KEY=WdpmKSRNxJhejBxxwvgtdGbPM0JBlRxm
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=adonis_tasks
DB_USER=YOUR_DB_USER
DB_PASSWORD=YOUR_DB_PASSWORD
Save the file and exit vim.
Next, we need to run database migrations. First, let’s install the Adonis CLI so we can make use of it great features:
npm i -g @adonisjs/cli
Once installed, let’s run the migrations:
    adonis migration:run --force
Because we are on production, we have to use the --``force flag.
Test The App
Before we process, let’s test to make sure the app is working fine. Start the app with the command below:
npm start
Then in a new terminal, enter the command below:
curl http://127.0.0.1:3333
You should get an output as below:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Task List</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.5.1/css/bulma.min.css" />
</head>
<body>
    <section class="section">
        <div class="container">
            <div class="columns">
                <div class="column is-8 is-offset-2">
    <div class="box">
          <h2 class="title">New Task</h2>

          <form action="/tasks" method="POST">
              <input type="hidden" name="_csrf" value="4f2uYvxk-j_OR-vX1D1_DfRb7KanGrhKwV-s">

              <div class="field has-addons">
                  <div class="control is-expanded">
                      <input class="input" type="text" name="title" value="" placeholder="Task title">
                  </div>
                  <div class="control">
                      <button type="submit" class="button is-primary">
                          Add Task
                      </button>
                  </div>
              </div>
          </form>
      </div>
      <div class="box">
          <h1 class="title">Task List</h1>
          <table class="table is-bordered is-striped is-narrow is-fullwidth">
              <thead>
                  <tr>
                     <th>SN</th>
                     <th>Title</th>
                     <th>Action</th>
                  </tr>
              </thead>
              <tbody>
                      <td colspan="3" class="has-text-centered">No task created yet!</td>
              </tbody>
          </table>
      </div>
                </div>
            </div>
        </div>
  </section>
</body>
</html>
This indicates the app is up and running fine. But whenever the app crashes we’ll need to manually start the app again which is not a recommended approach. So, we need a process manager to help us with starting the app and restarting it whenever it crashes. We’ll use PM2 for this.
Install PM2
We’ll install it globally through NPM:
sudo npm install -g pm2
With PM2 installed, we can start the app with it:
pm2 start server.js
Once the app is started you will get an output from PM2 indicating the app has started.
To launch PM2 on system startup or reboot, enter the command below:
pm2 startup systemd
You’ll get the following output:
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/local/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd -u mezie --hp /home/mezie
Copy and run the last command from the output above:
sudo env PATH=$PATH:/usr/local/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd -u mezie --hp /home/mezie
Now PM2 will start at boot up.
Set Up Nginx as a Reverse Proxy Server
Finally, we set up Nginx as a reverse proxy server. To this, run:
sudo vim /etc/nginx/sites-available/default
Within the server block you should have an existing location / block. Replace the contents of that block with the following configuration:
// /etc/nginx/sites-available/default

...
location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://127.0.0.1:3333;
    proxy_set_header Host $http_host;
    proxy_cache_bypass $http_upgrade;
    proxy_redirect off;
}
Save and exit vim.
Test to make sure there are no syntax errors in the configuration by running:
sudo nginx -t
Then restart Nginx:
sudo systemctl restart nginx
Now you should be able to access the app with your IP_ADDRESS. You should get something similar to the image below:
Conclusion
That’s it. You have seen how to deploy AdonisJS app to Digital Ocean.