Node.js app continuous deployment with AWS, PM2, and Github Actions.
Tags: aws, node, github, git
Published: 2020-12-08 14:51:39

So you have this **node.js** app. It's not static, it's in Vue, React, Angular, whatever... It's ready and steady to publish and deploy. But where, how? In this article, I'll cover some steps on how to automatically deploy your app and keep it delivered continually.

Here is a quick summary:
1. [Runing EC2 instance on AWS](#aws)
2. [PM2 setup](#pm2)
3. [Deployment script](#script)
4. [GitHub Action](#github)

Digression: If your app does not have any backend-related functionalities I'm almost sure that it can be built with static mode. So for that, you can use ie [Netlify](https://netlify.com) and skip this thread. 🙃 

**Static apps generate a smaller carbon footprint**. 

OK, let's back to the main topic.

-----

**1. Runing EC2 instance on AWS.** [](https://dev.to/lukasborawski/node-js-app-deployment-with-aws-pm2-and-github-actions-31o2#aws)

For that case, we will use EC2 on AWS. If you're not familiar with the installation process all you need to do is:
* [create an account on AWS](https://aws.amazon.com/)
* [launch your EC2 Instance](https://docs.aws.amazon.com/efs/latest/ug/gs-step-one-create-ec2-resources.html)
* [create EC2 key pairs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html)

Don't worry, it's super simple and we don't need to move through. Just watch the guidelines and you'll be fine. However, maybe for you, the Digital Ocean Droplet will be the best solution; or any other. Use what you like the most, we just need a regular Ubuntu instance. However, note that paths in the below configs are AWS specific.

Now, log in to your newly created instance.

```bash
ssh -i key.pem ubuntu@12.345.67.891
```

For sure - for the new machine - you’ll need to configure the webserver, most likely Nginx. More about this you can find [here](http://nginx.org/en/docs/beginners_guide.html) or just use this predefined one.

```bash
$ sudo vim /etc/nginx/conf.d/nodejs-basic.conf
```

```bash
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 80;

    server_name your-server-name.com;

    location / {
         proxy_pass http://localhost:3000;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection 'upgrade';
         proxy_set_header Host $host;
         proxy_cache_bypass $http_upgrade;
    }
}
```

**2. PM2 setup.** [](https://dev.to/lukasborawski/node-js-app-deployment-with-aws-pm2-and-github-actions-31o2#pm2)

PM2, what the heck? [PM2](https://pm2.keymetrics.io) is a daemon process manager that will help you manage and keep your application online 24/7. So it will help keep our app live and running. Let’s install it.

```bash
npm install pm2 -g
```

Then, in your home folder create a simple configuration file (`eccosystem.json`).

```json
{
  "apps" : [
     {
        "name"          : "App Name",
        "script"        : "npm",
        "args"          : "run start",
        "watch"         : false,
        "cwd"           : "/home/ubuntu/app-name/",
        "out_file"      : "/home/ubuntu/hooker.out.log",
        "err_file"      : "/home/ubuntu/hooker.err.log",
        "merge_logs"    : true,
        "instances"     : 1,
        "exec_mode"     : "cluster"
     }
  ]
}
```

**3. Deployment script.** [](https://dev.to/lukasborawski/node-js-app-deployment-with-aws-pm2-and-github-actions-31o2#script)

Therefore we need some simple bash script (`deploy.sh`) that will run the app build and invoke PM2 process after that.

```
echo "Kill all the running PM2 actions"
sudo pm2 kill

echo "Jump to app folder"
cd /home/ubuntu/app-name

echo "Update app from Git"
git pull

echo "Install app dependencies"
sudo rm -rf node_modules package-lock.json
sudo npm install

echo "Build your app"
sudo npm run build

echo "Run new PM2 action"
sudo cp /home/ubuntu/ecosystem.json ecosystem.json
sudo pm2 start ecosystem.json
```

You can test it by using `sh deploy.sh` command.

Great, we're almost there. Server configuration is relatively simple and you can make it almost effortless. If your daily environment is MacOS or Linux it will be very straightforward. 

Now - just for the test - we can run the whole process manually. To do this follow the below steps.

* configure webserver
* clone/fetch your app on the instance
* build the app 
* install PM2 manager
* run `sudo pm2 start npm --name "process" -- start`

Or set it as a simple checklist for the instance setup.

**4. GitHub action.** [](https://dev.to/lukasborawski/node-js-app-deployment-with-aws-pm2-and-github-actions-31o2#github)

Finally, we can create a GitHub action that will run our deployment process. Just for our case, we will create an action that will be called for each merge to the `develop` branch.  

In your main app directory create the `.github` folder inside which the `workflows` one. Then create a `deploy.yaml` file. 

```yaml
name: Deploying

on:
  push:
    branches:
      - develop

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest

    steps:
      - name: executing remote ssh commands using ssh key
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.AWS_HOST }}
          username: ${{ secrets.AWS_USER }}
          key: ${{ secrets.AWS_KEY }}
          script: sh deploy.sh
```

As you can see we have some variables here. These are GitHub specific secrets. You can define them in your account settings. Values for AWS-related credentials can be found in the console. [Here](https://aws.amazon.com/blogs/security/wheres-my-secret-access-key/) you have some detailed instructions. 


**Remember! Always keep sensitive data outside the repository.**

And that's it. Push the workflow into your repo and wait for your first automatic deployment. It will take circa a couple of minutes (max). Done.

Cheers, Lukas.  

<a href="https://www.buymeacoffee.com/lukas.borawski" target="_blank"><img src="https://miro.medium.com/max/1400/1*CEZSIxeYr6PCxsN6Gr38MQ.png" alt="Buy Me A Coffee"></a>