Howie Mann

An ex-banker's view of software, finance and growth marketing.

How to Deploy a Node App to AWS EC2

January 13, 2020 | 7 minute read

node security infrastructure

This walkthrough will show you how to deploy a containerized Node.js app to Amazon Web Services and also assign a secure custom domain. We will be applying the following technologies and tools:

Click here for a link to the github repo

Summary Steps

  1. Create Node App with Dockerfile and deploy to Github
  2. Create EC2 Instance
  3. SSH into EC2 Instance
  4. Setup and Install EC2 Dependencies
  5. Create Route 53
  6. Purchase Namecheap Domain
  7. Setup Nginx and HTTPS

1. Create Node App with Dockerfile and deploy to Github

Create a simple Node Express app with the following folder structure

src
  L index.js
Dockerfile
package.json

Create a simple hello world web app.

// src/index.js
let express = require('express')
const PORT = process.env.PORT || 3000

let app = express()

app.get('/', (req, res) => {
  res.send('hi')
})

// Custom 404 route not found handler
app.use((req, res) => {
  res.status(404).send('404 not found')
})

app.listen(PORT, () => {
  console.log(`Listening on PORT ${PORT}`);
})

Create Dockerfile which will install specific version of node, install dependencies, expose relevant port and run start command.

# // Dockerfile

# Select node version and set working directory
FROM node:8-alpine
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Install app dependencies
COPY package.json /usr/src/app
RUN npm install

# Bundle app source
COPY . /usr/src/app

# Expose publc port and run npm command
EXPOSE 3000
CMD ["npm", "start"]

Test locally by building the Docker image (check Docker desktop app is running).

docker build . -t <YOUR_APP_NAME>

Test locally by running the app. Expose port 3000 (we will later use Nginx reverse proxy to send HTTPS port 80 requests to localhost:3000)

docker run -p 3000:3000 <YOUR_APP_NAME>

Commit and deploy to a Github repo where we will later clone from our EC2 Instance.

git add .
git commit -m "initial commit"
git remote add origin "<YOUR_GITHUB_REPO_URL>"
git push origin master

2. Create EC2 Instance

Create a new AWS EC2 Instance and set a fixed public IP.

3. SSH into EC2 Instance

Login to our newly created EC2 Instance via SSH.

4. Setup and Install EC2 Dependencies

Clone github repo, install Docker, build image and run new image.

ssh -i "<YOUR_PRIVATE_KEY_LOCATION_FILE.pem> <YOUR_USER>@<YOUR_AWS_PUBLIC_DNS>
git clone <YOUR_GITHUB_REPO_URL>
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
cat /etc/passwd | grep ubuntu
sudo usermod -aG docker ubuntu
docker ps
docker build . -t <YOUR_APP_NAME>
docker run --restart always -d -p 3000:3000 -t <YOUR_APP_NAME>
docker ps
docker logs <YOUR_CONTAINER_ID>
docker stop <YOUR_CONTAINER_ID>

5. Create Route 53

Setup AWS Route53 for DNS forwarding from our custom domain to relevant EC2 Instances.

6. Purchase Namecheap Domain

Purchase custom domain and point to AWS Route 53.

7. Setup Nginx and HTTPS

Nginx reverse proxy port 80 to our Docker instance and setup secure HTTPS.

sudo apt-get install nginx
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get install python-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo rm /etc/nginx/sites-enabled/default
cd /etc/nginx/sites-available/
sudo touch node
sudo vim /etc/nginx/sites-available/node
server {
  listen 80;
  listen [::]:80;
  server_name example.com www.example.com;
  return 301 https://$server_name$request_uri;
}

server {
  listen 443 ssl http2 default_server;
  listen [::]:443 ssl http2 default_server;
  server_name example.com www.example.com;

  location / {
    proxy_pass http://localhost:3000;
  }

  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
  ssl_session_cache shared:SSL:5m;
  ssl_session_timeout 1h;
  add_header Strict-Transport-Security “max-age=15768000” always;
}
sudo ln -s /etc/nginx/sites-available/node /etc/nginx/sites-enabled/node
sudo service nginx restart
sudo fuser -k 80/tcp
sudo service nginx start

That's it.

You should now be able to visit your custom domain and be pointed to your Dockerized Node App on HTTPS.