Deploy a node app to AWS EC2, set up nginx server and configure https using letsencrypt certbot
Introduction
This article is a step-by-step guide to deploy a NodeJS app to AWS EC2 we will use the following tools or technologies:
- NodeJS + Express for the application
- AWS EC2 amazon-linux instance as the server
- nginx to set up reverse proxy
- letsencrypt certbot for security certificate
I am using macos, so other system users may need to make some changes as needed.
Prerequisites
- A github repository with a working nodejs application.
- Basic knowledge of command line, git and github.
- A domain name that you own (this domain should replace any instance of your.domain.com in this article)
If you do not have one, checkout my previous article.
Or directly fork my git repository.
Set up EC2 Instance
- Go to AWS and login or create a new account
- On the top left, select Services > Compute > EC2
- Click on "Launch Instance" and select "Launch Instance"
- Select a name of your choice, I am choosing
simple-node-app-server
- Under Application and OS, Select Amazon Linux and keep the other options default
- Under Instance Type, select t2.micro
- Under Key pair (login), select Create new key pair
- Enter any name of your choice, I am choosing
simple-node-server
- Choose Key pair type RSA
- Choose Private key file format .pem, click create and then save it on your system somewhere safe. This is a secret file and someone can access your server if they have access to this.
- Note the path to the file you can find this by right clicking on the .pem file and clicking info. This will be something like
/Users/your_user_name/path/to/folder/simple-node-server.pem
- Enter any name of your choice, I am choosing
- Under network settings > Firewall, select Create security group and check all three options around allow ssh traffic, allow http traffic and allow https traffic. You might see an warning but this is fine, we will change this later.
Now click launch instance to start the EC2 instance, you should see a success message and the link to your new instance. Click on that to land on your instance details page.
- On the instances page, click on the instance id you just created
- On top right, click on connect and select SSH CLient, follow the instructions to connect to your instance via SSH
- Run
sudo yum update -y
to update everything
Now the instance set up is done and we are able to interact with the instance using SSH.
Clone repo from github
Set up SSH Keys and connect to github
SSH into your instance, then:
- Run
ssh-keygen -t ed25519
, in subsequent prompts select the default location and enter a passphrase twice. This passphrase is needed so remember it. - Run
sudo nano ~/.ssh/id_ed25519.pub
and copy the contents of the public key, exit nano without saving by pressingctrl
andx
together. - Go to github > Settings > SSH and GPG Keys (URL)
- Click New SSH Key, select a title of your choice, select key type "Authentication Key" and add the contents of the public key in the text box then press Add SSH Key.
Now we are ready to connect to github using SSH.
Install node, npm and git
Run the following commands
#Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash
#Enable nvm command
source ~/.bashrc
#Install node v16
nvm install 16
#Install git
sudo yum install git -y
Clone your github repository by running
# replace the part after "git clone" with your actual github repo endpoint.
git clone git@github.com:yourusername/simple-node-app.git
Set up the security group of the instance
- Go to AWS EC2 Console then click on instances and then your instance id to land on the instance summary page
- Scroll down and select the security tab
- Under security groups click on the link available
- Scroll down and click edit inbound rules
- It should already have HTTP, HTTPS and SSH configured. If not add them and select source anywhere
- Add a new rule of type custom TCP select port range 8080 or whatever port your app uses
- Click on save rules to save the same
- Go to your instance summary page again and copy the public IPV4 address. We will need this in the next step to replace
your.public.ip.here
Start the app
SSH into your instance
- cd into the app by running
cd simple-node-app
- Run
npm i
to install packages - Start your app by running
node index.js
or whatever is your app's start script.
Now go to a browser and go to http://your.public.ip.here:8080
and you should land on your app.
You can also choose to close out ssh applications to your server from any other ip but yours. You can do this by changing the source option of SSH from 0.0.0.0/0
to your ip. To find your ip go back to the ssh shell where you are connected to the instance and run who am i
- Go back to SSH shell and quit the server using
ctrl
+c
- install pm2 by running
npm install pm2 -g
- Run your app again by running
pm2 start index.js
. Replace index.js with your app's endpoint.
Now the app will run even when you exit the shell command.
Set up nginx and certbot
We do not want to pass the PORT with the address when we deply. We would rather expose our 8080 port to the root IP address. To do this, we will use nginx.
SSH into your instance, then run the following commands
#Install nginx
sudo amazon-linux-extras install nginx1 -y
#Enable nginx
sudo systemctl enable nginx
#Start nginx
sudo systemctl start nginx
#Check nginx status
sudo systemctl status nginx
Now, go to your AWS Console EC2 instance and copy the public IP address and on any browser go tohttp://your.public.ip.here
.
We should see the welcome page of nginx.
Note, above you must use http and not https as we have not set up ssl yet.
Edit nginx config
Now we will set up our nginx config to redirect http requests to https and allow https connections
- Run
sudo nano /etc/nginx/nginx.conf
to start editing the config file - Remove everything from the file and put the following:
Replace your.domain.com with your actual domain name and under location, instead of 8080 use the PORT that your application uses
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
include /etc/nginx/conf.d/*.conf;
# Redirect http requests to https
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
# Set up TLS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your.domain.com;
ssl_certificate "/etc/letsencrypt/live/your.domain.com/fullchain.pem";
ssl_certificate_key "/etc/letsencrypt/live/your.domain.com/privkey.pem";
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:8080;
}
}
}
Add DNS A Record
Go to your domain service provider and set up an A record which points to your EC2 Instance's public ipv4 address
Set up certbot
Certbot is a free, open-source software tool for automatically using Let's Encrypt certificates on manually-administrated websites to enable HTTPS.
- SSH into your instance
- If you are already there, make sure you are in the root directory, if not -
cd ~
Run the following commands
#Download EPEL
sudo wget -r --no-parent -A 'epel-release-*.rpm' https://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/
#Install the repository packages
sudo rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm
#Enable EPEL
sudo yum-config-manager --enable epel*
#Install certbot
sudo yum install -y certbot
#Install certbot-nginx
sudo yum install -y python-certbot-nginx
Now we have installed the certbot client and we will use it to create a certificate.
In the next command, make sure to replace your.domain.com with your actual domain name that youa re going to use
- Run
sudo certbot certonly --standalone --debug -d your.domain.com
Now we have a TLS certificate.
- Run
sudo systemctl restart nginx
to effect the changes.
Now if you go to your.domain.com
you should see your app deployed to EC2.
Set up cronjob to auto renew certificate
We will set up a cron job to try to auto renew the certificate every day at 12 noon.
- SSH into your instance
- Run
sudo crontab -e
- To start editing, press esc and then press i
- paste the following into the editor
0 12 * * * /usr/bin/certbot renew --quiet
- Press esc
- type
:wq
and press enter
Now we are all set.