You are on page 1of 12

17/02/2023 11:39

Andrew McCallum
Posted on 10 de dez. de 2021 • Updated on 4 de abr. de 2022

Deploy your Node App to EC2 with Github Actions


In this post we are going to run through how to deploy our code to AWS EC2 via Github
Actions. This will include the following steps:
1. Create EC2 instance
2. Configure IP and domain name
3. SSH into our server
4. Install NGINX
5. Install Node and PM2
6. Deploy with Github Actions
Create EC2 instance
We are going to need to launch a new instance, create an SSH key pair and configure
the correct security groups.
I wont bore you with the detail of going through the AWS EC2 creation wizard but you
can go ahead and create a new EC2 instance with an Ubuntu machine image with a t2
micro and download your new keys.
Make sure you download your ssh keys and store them somewhere secure as you will
need them again later.
https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 1/12
17/02/2023 11:39

We have created our new instance so whilst it is booting up, we are going to configure
a security group. This will allow us to accept incoming and outgoing network reqeusts
on specific ports and IP addresses.
Go to the Security Groups page and then select "Create security group".

https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 2/12
17/02/2023 11:39

We are going to allow (port 80) and HTTP (port 443) on both our incoming and
HTTPS
outgoing connections. This means we can access our app via http or https and we can
call API's from our app on both those protocols too. Keep in mind ssh (port 22) is
enabled by default.
If your app is connecting to a database, you may want to allow rules for PostgreSQL or
MySQL etc.

Configure IP and domain name


https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 3/12
17/02/2023 11:39

Here we will assign an IP address to our EC2 instance and then update our domain's
name server to point the domain name to our new IP.
Within the EC2 service section of AWS, navigate to Elastic IPs. Here we can click
"Allocate Elastic IP address" to generate a new IP address. We can now assign that to
the newly created EC2 instance we just created by clicking actions > Associate elastic
IP address and selecting our EC2 instance.

Next we will point our domain name to our new IP address. In my case, my domain
name uses AWS Route53 as a name server so I will navigate there and create an A
record to point .
<my_ip_address> -> www.my-website.com

SSH into our server


Next up we are going to access our EC2 instance via SSH. In oder to do this, we will do
the following:
1. Open an SSH client.
2. Locate your private key file
3. Run this command, if necessary, to ensure your key is not publicly viewable.
$ chmod 400 My-ssh-key.pem

4. Connect to your instance using its Public DNS:


$ ssh -i "My-ssh-key.pem" ubuntu@<host>.compute.amazonaws.com

https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 4/12
17/02/2023 11:39

You should now have SSH access to your EC2 server.


Install NGINX
After ssh'ing into our server, we will need to do the following; install NGINX, configure
firewall, forward web traffic ports and lastly set directory permissions.
$ sudo apt update
$ sudo apt install nginx

We are then going to adjust our firewall to allow http and https connections to our
NGINX server.
$ sudo ufw allow 'Nginx Full'

We should then have an active web server:


$ systemctl status nginx

https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 5/12
17/02/2023 11:39

If we visit http://<my_ip_address> we should see the NGINX welcome page.

We now know our web server is accepting connections on port 80. Our Node app is
going to be running on port so we are going to need to forward connections from
3000
port to . This will be done via our NGINX config in the sites-available file.
80 3000

$ sudo nano /etc/nginx/sites-available/my-app

We are going to paste in this config:


upstream my_nodejs_upstream {
server 127.0.0.1:3000;
keepalive 64;
}

server {
listen 80;
# listen 443 ssl;

server_name www.my-website.com; # This is the domain name we set up earlier


# ssl_certificate_key /etc/ssl/main.key;
# ssl_certificate /etc/ssl/main.crt;

location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 6/12
17/02/2023 11:39

proxy_pass http://my_nodejs_upstream/;
proxy_redirect off;
proxy_read_timeout 240s;
}
}

This listens for traffic for (this will be the domain you set up earlier
www.my-website.com
to point to your IP address) on port 80 and forwards it to our node app on port 3000.
As you can see, I have commented out the SSL config but if you have an SSL cert, feel
free to use that instead.
We are now going to need to symlink this file into sites-enabled directory.
$ sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled

We can go ahead and restart NGINX to make all these changes take effect.
$ sudo systemctl restart nginx

Lastly for this step, we are going to create a file for our application files to live in and
set the correct permissions.
$ sudo mkdir /var/www/my-app

Change the directory owner and group:


$ sudo chown www-data:www-data /var/www/my-app

Allow the group to write to the directory with appropriate permissions:


$ sudo chmod -R 775 /var/www

Add myself to the www-data group:


$ sudo usermod -a -G www-data [my username]

We should now have NGINX installed with a config that forwards incoming requests on
port 80 to port 3000 and have a directory created to contain our site files with all the
correct permissions.
Install Node and PM2
https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 7/12
17/02/2023 11:39

We are going to install Node via NVM so that we can easily change node versions and
then we will use to install . npm pm2

1. To install : nvm

$ curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | b
$ source ~/.profile

2. Install the version of Node you want


$ nvm install 14

3. We should now have Node and NPM installed which we can now use to install PM2.
$ npm install pm2 -g

If we had our code deployed to our server, we would then be able to start our app via
PM2 but we will have to come back to this once our CI pipeline is complete.
Deploy with Github Actions
Now we can finally create a deployment pipeline in Github actions that automatically
deploys our code to our new server (into the directory we created with the correct
permissions). We will then move back to our ssh terminal in order to start our app.
Before we deploy anything, we are going to upload our previously downloaded ssh
private key, into Github secrets so that we can reference it in our CI pipeline.
We are going to name ours : SSH_PRIVATE_KEY

Using the following yaml file, we do the following:


https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 8/12
17/02/2023 11:39

1. install dependencies
2. run build
3. run tests
4. use rsync to copy files to our server (it will replace existing files)
name: CI

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

workflow_dispatch:

jobs:
build:

runs-on: ubuntu-latest

if: github.ref == 'refs/heads/master'

strategy:
matrix:
node-version: [14.x]

steps:
- uses: actions/checkout@v2

- run: npm ci
- run: npm run build
- run: npm run test

- name: rsync deployments


uses: burnett01/rsync-deployments@5.1
with:
switches: -avzr --delete
path: ./*
remote_path: /var/www/my-app/
remote_host: <host>.compute.amazonaws.com
remote_user: ubuntu
remote_key: "${{ secrets.SSH_PRIVATE_KEY }}"

Push this to our master branch in our Github repo and we should see an action run. If
successful, our files should now be on our server in . /var/www/my-app

https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 9/12
17/02/2023 11:39

If we switch back to where we're ssh'd into our server, we can now start our app.
$ pm2 start /var/www/my-app/dist/index.js start --watch

In my case, when I run it outputs my built files into the


npm run build dist
directory. You may have a different path to where you start your app from.
Also, by adding we allow pm2 to restart the service whenever the files
--watch
change. I.e. after each deployment.
If you need to set any environment variables, you can do so like so (keep in mind
you will need to restart your app afterwards).
$ export MY_ENV_VAR=foo_bar

We should now have successfuly setup NGINX, installed node (via NVM) and PM2 and
setup a deployment pipeline in Github actions.
With any luck, our application should be available to the public via http://www.my-
website.com or whatever domain name you configured.
Shout out in the comments if you have any questions or issues following this guide and
I'll do my best to answer them.
Happy coding and look forward to any any feedback.
Top comments (2)
Jendorski • 21 de abr. de 22
This was very helpful, thank you very much

bitsbysalih • 25 de ago. de 22
What would the config file look like if it was being setup for a nestjs app instead of plain
old nodejs
Code of Conduct • Report abuse

Here is a post you might want to check out:


https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 10/12
Regex for lazy developers
17/02/2023 11:39

Sorry for the callout 😆

Andrew McCallum
Keeping it simple
LOCATION
London, United Kingdom
WORK
Lead Software Engineer
JOINED
21 de set. de 2021

More from Andrew McCallum


Is React Testing Library a Suitable Replacement for Enzyme?
#javascript #webdev #testing #react

Work From Home or Work From the Office. Which is Better?


#watercooler
https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 11/12
17/02/2023 11:39

A grid layout with responsive squares


#css #html

https://dev.to/stretch0/deploy-your-node-app-to-ec2-with-github-actions-h9a 12/12

You might also like