TutorialsLast Updated Aug 26, 20248 min read

Deploy a Node app on AWS EC2 Linux

Olususi Oluyemi

Fullstack Developer and Tech Author

Developer A sits at a desk working on an intermediate-level project.

Amazon Web Services (AWS) provides a vast ecosystem of products that make DevOps an absolute dream. Products like AWS Elastic Beanstalk have ready-made services for autoscaling, deployment, and logging (to name a few). However, for teams that prefer to take a barebones approach and build incrementally, AWS Elastic Compute Cloud (EC2) may be a better option.

Regardless of the path chosen, CircleCI provides a robust platform that you can use to automate the deployment process and reduce bottlenecks in your software development lifecycle. In this article, I will show you how to deploy a Node application on an AWS EC2 Linux instance and automate the deployment of further updates by pushing to GitHub.

Prerequisites

In addition to a basic understanding of JavaScript, you will need the following to get the most from this tutorial:

Setting up an EC2 server

Launch new EC2 instance from your AWS dashboard.

Provide a descriptive name for your instance and select Ubuntu from the Quick Start section. A sample selection is shown below.

New EC2 instance

Scroll down to create a new key pair for authentication. If you already have an existing key pair, you can select it from the dropdown.

Warning: Do not proceed without creating or selecting a key pair as you will not be able to connect to your instance.

Create a new key pair

If you created a new key pair, a .pem or .ppk file will be generated and made available for you to download. Make sure you store it somewhere memorable.

Next, move down to the Network section. Update the firewall security groups section to create a new security group that allows SSH traffic from anywhere, allows HTTPS traffic from the internet, and allows HTTP traffic from the internet.

Update firewall security

Finally, click Launch Instance in order to complete the creation process.

Once this is completed, you will be redirected to view your list of Instances. Select your newly created instance and look out for the Public IPv4 DNS, which will be used in connecting to your instance

View instance

For the sake of brevity, this tutorial will not be covering how to set up a reverse proxy using Nginx. Instead, we will use the less secure option of opening port 8000 on the EC2 instance that the application is running on.

To do this, select the Security tab from the bottom menu bar. Next, CMD+Click on the hyperlinked name of the security group mentioned under the Security groups sub-heading. This will open your security group in a new tab.

Security tab

In the newly opened tab, click the Edit Inbound rules button at the bottom right hand of the Inbound Rules section to see the inbound rules that control the incoming traffic to the instance:

Edit inbound rules

Click Add rule and add the following new inbound rule for port 8000.

Add custom rule

Save the new rule to allow connections to port 8000 of your EC2 instance.

With that, you have successfully initiated the EC2 instance and allowed all traffic to the ports that you will need to run the NodeJS application.

Setting up the NodeJS application on EC2

Next, go to the directory where you have downloaded or have the AWS Key file.

You first need to set the certificate permissions to 600. Without doing this, you will not be able to connect to your instance. To modify the permissions, execute the following command:

chmod 600 <name-of-your-keypair>.pem

Then, run the following command:

ssh -i <key-pair-name>.pem ubuntu@<instance-public-dns-name>

Substitute the <key-pair-name> with the name of the Key file and replace <instance-public-dns-name> with your public DNS name. This can be found in the Details tab under the Public IPv4 DNS section.

Note: For the first time, you will encounter a confirmation message regarding the SSH key. Type yes to proceed.

Once you have connected to your instance, install Node and npm on it.

sudo apt update

sudo apt install nodejs -y

sudo apt install npm -y

The above commands will update Ubuntu in the instance and install NodeJS and NPM packages in the instance.

Clone the sample project from GitHub onto the intstance using the following command:

git clone https://github.com/CIRCLECI-GWP/node-aws-sample

This command will create a new directory and download all the files hosted in the repository into the folder.

Finally, you can run the application using the following command:

node node-aws-sample/server.js

To view the application, open http://<instance-public-ipv4-address>:8000 in your browser.

View application on AWS

Configuring NodeJS server for production

To allow the server to run as an independent process in production, we’ll use pm2, a process manager for Node.js applications. It allows you to keep your applications running smoothly and to automatically restart them in the event of a crash or system restart.

Stop the application and install pm2 on the instance using the following commands:

mkdir ~/.npm-global

npm config set prefix '~/.npm-global'

export PATH=~/.npm-global/bin:$PATH

source ~/.profile

sudo npm install -g pm2

This will install and configure the pm2 package in your system.

Next, restart the application using the following command, from outside the project folder.

pm2 start node-aws-sample/server.js --name "node_app"

With this command, the application will still be running even if the SSH connection is terminated.

Next, you need to create a bash script that will be called to handle the deploy process on the EC2 instance. Make sure the file is created outside the project directory.

vim deploy.sh

Add the following to deploy.sh.

#!/bin/bash

cd ~/node-aws-sample

git pull origin main

mkdir ~/.npm-global

npm config set prefix '~/.npm-global'

export PATH=~/.npm-global/bin:$PATH

source ~/.profile

pm2 restart node_app

Finally, save this file.

This script pulls and navigates into the project directory, pulls the latest code, and then restarts the node application using pm2.

Make the script executable by running the following command.

chmod u+x deploy.sh

Next, in your local terminal create a new SSH key which will be used in your CircleCI pipeline to connect to your EC2 instance. Do it using the following command.

ssh-keygen -m PEM -t rsa -f ~/.ssh/circleci

Since CircleCI cannot decrypt SSH keys, every new key must have an empty passphrase. Press Enter twice to create a key without a passphrase. Print the new public key with the following command.

cat ~/.ssh/circleci.pub

Copy the public key so that you append it to the existing authorized keys in your EC2 instance. You can edit the list of existing authorized keys using the following command:

vim .ssh/authorized_keys

With that in place, our EC2 instance is ready to accept SSH connections and also runs a bash script to pull the latest changes in our application.

Next, we’ll set up CircleCI to initiate the deployment process once a new push is made to the repository.

Adding the CircleCI configuration file

The downloaded demo project already contains the configuration file required to successfully set up the deployment pipeline for our project. Locate the config.yml file within the .circleci folder and ensure that its content is similar to the following:

version: 2.1

jobs:
  deploy:
    docker:
      - image: arvindr226/alpine-ssh
    steps:
      - run: ssh -oStrictHostKeyChecking=no -v $USER@$DNS "./deploy.sh"

workflows:
  deploy-to-ec2:
    jobs:
      - deploy

This config has one job named deploy. This job runs Alpine-ssh, a minimal Docker image that enables SSH. It runs a single command which is to SSH into the EC2 instance and runs the [deploy.sh](http://deploy.sh) script.

Connecting the application to CircleCI

The next step is to set up a repository on GitHub and link the project to CircleCI. Clone the repository to your local machine and push it to your own GitHub repository.

Log in to your CircleCI account. If you signed up with your GitHub account, all your repositories will be available from the Organization Homepage after you click Set Up Project.

Click Set Up Project next to your node-aws-sample project.

Select project

You will be prompted to enter the name of the branch where your code is housed on GitHub. Click Set Up Project once you are done.

Select config

Your first workflow will start running.

The deploy job will fail because we are yet to add an SSH key to the project and set the required environment variables.

To fix that, start by adding two environment variables for USER and DNS. Click Project Settings.

Click the Environment Variables button on the left sidebar and create these variables:

  • The USER variable is the username to be provided in the ssh command. In this case it is ubuntu.
  • The DNS variable is the public IPV4 DNS of your EC2 instance.

Add env vars

Next, click SSH Keys on the sidebar. Scroll down and click Add SSH Key

Add SSH key

For the hostname, use the public IPV4 DNS for your EC2 instance. To get the private key, run the following command in your local terminal.

cat ~/.ssh/circleci

Copy the output (starting from *** ----BEGIN RSA PRIVATE KEY---— *** and ending with ----END RSA PRIVATE KEY---— **) and paste in the Private Key section. Click Add SSH Key to save the new key.

Go back to the dashboard. Click Rerun Workflow from Start inside the actions row on the failed pipeline run.

Your workflow will run successfully.

Successful build

To make sure that everything works properly, let’s make an update to the application. For example, you can modify the name of a user within the users.js file.

Commit and push the changes to your GitHub repository and then head back to your CircleCI dashboard. Once the workflow completes running, you can try out the new route in your browser to validate that your change deployed automatically.

Conclusion

In this article, we looked at how to implement a deployment pipeline using CircleCI for an Amazon EC2 instance. To keep things simple, we took an approach that focused solely on the deployment pipeline. There are two key things to note when replicating this approach in a production environment:

  1. Exposing ports on your server over the internet could have serious security implications. A more secure approach would be to use a reverse proxy server (such as Nginx) to handle incoming requests to the DNS and redirect as required.
  2. To SSH into our EC2 instance, we used the ubuntu user. This user has sudo privileges and should not be used for the SSH command in CircleCI. A safer approach would be to create a non-sudo user and use that in connecting with the instance.

The entire codebase for this tutorial is available on GitHub.


Oluyemi is a tech enthusiast with a background in Telecommunication Engineering. With a keen interest in solving day-to-day problems encountered by users, he ventured into programming and has since directed his problem solving skills at building software for both web and mobile. A full stack software engineer with a passion for sharing knowledge, Oluyemi has published a good number of technical articles and blog posts on several blogs around the world. Being tech savvy, his hobbies include trying out new programming languages and frameworks.

Copy to clipboard