This is a personal-use post for hosting a full-stack website for public access. Everytime I wanted to deploy a new website, I had to go through several tutorials and stuck in the same problems over and over again. Therefore, I decide to record the whole process in this post based on my previous experience, so that I can deploy my website smoother in the future.

(P.S. Screenshots will be uploaded in the near future)

Backend

The backend will be hosted on AWS EC2. First, check if your AWS account still has enough free tier usage (i.e., it’s in the first 12 month). If you can afford, never mind. Otherwise, create a new account.

Security Group

Two SG should be created: one for ssh connection, one for internet access

ssh-sg:

  • inbound: SSH-TCP-22-Custom-0.0.0.0/0
  • outbound: All traffic-All-All-Custom-0.0.0.0/0

internet-sg:

  • inbound: Custom TCP-TCP-4000-Custom-0.0.0.0/0, HTTP-TCP-80-Custom-0.0.0.0/0, HTTPS-TCP-443-Custom-0.0.0.0/0
  • outbound: All traffic-All-All-Custom-0.0.0.0/0

EC2 instance

Linux, t2.micro, select a key pair, put 2 SGs in, launch.

After the instance is successfully launched, connect it via SSH. (in aws/local terminal)

Initialize machine

Run the following code to update the environment:

sudo apt update
sudo apt install nodejs -y
sudo apt install npm -y
sudo npm install --global yarn
sudo npm i ts-node --g
sudo snap install docker
sudo docker version     # check if success

Repository download

Generate an SSH Key for GitHub access:

ssh-keygen

Then paste the public key into GitHub Settings.

Select a location to clone the GitHub repository. Remember to check some configuration such as the base url and port.

Direct to that folder.

Add a .env file if your app uses environment variables. Then go into the file and paste in your environment variables.

touch .env
sudo nano .env

First Test

Run the application. The terminal should show success message. We cannot access through the ip yet, since the port isn’t accessible right now.

npm install
node server.js

If you encounter this problem:

            session: options?.session,
                             ^
SyntaxError: Unexpected token '.'

Just update your Node.js:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

nvm ls-remote
nvm install <version>
nvm use <version>

Add NGINX with Docker

NGINX: a highly popular open-source web server, reverse proxy server, load balancer, and HTTP cache.

Go to root directory, install NGINX, and go to that directory.

cd ~
git clone https://github.com/coderaidershaun/nginx-with-docker.git
cd nginx-with-docker
sudo nano nginx.conf # modify the config file

Modify the conf file following the comment, change all the “frontend” part to “backend”. Then build the docker image(might take some time):

tips for nano: save&exit is Ctrl+O, Enter, Ctrl+X

sudo docker build -t nginx-with-docker . --no-cache

Permanently running service

We need to keep the NGINX and backend running all the time even after system reboot.

For NGINX:

cd ~
sudo nano /etc/systemd/system/reverseproxy.service

Paste this in the service file:

[Unit]
Description=Reverseproxy Service
After=network.target
Wants=network.target

[Service]
Type=simple
Restart=always
RestartSec=5
ExecStart=sudo docker run -p 80:80 nginx-with-docker

[Install]
WantedBy=default.target

Then start the service:

sudo systemctl enable reverseproxy
sudo systemctl start reverseproxy
sudo systemctl status reverseproxy

The status should show “active (running)” in green.

Other useful code for debug:

sudo systemctl daemon-reload
sudo systemctl stop reverseproxy
sudo journalctl -fu reverseproxy.service

For Backend:

cd ~
sudo nano /etc/systemd/system/backend.service

You should find the path of your new version of node.js using “whereis node”. Then remember to change the path in below.

Paste this in the service file:

[Unit]
Description=Backend Service
After=network.target
Wants=network.target

[Service]
Type=simple
Restart=always
RestartSec=5
ExecStart=/home/ubuntu/.nvm/versions/node/v20.11.1/bin/node /home/ubuntu/movipendent/backend/server.js

[Install]
WantedBy=default.target

Then start the service:

sudo systemctl enable backend
sudo systemctl start backend
sudo systemctl status backend

Second test

Now access your endpoint through HTTP, you should see your server working.

Frontend

Somehow my EC2 t2.micro always fails when I try to run frontend and backend simultaneously, so I turn to Vercel to host my frontend.

The steps on Vercel are quite straight-forward, just connect to your gitHub repository and modify the backend url in config file. The most critical issue is to establish a connection between Vercel as HTTPS to EC2 instance as HTTP.

API Gateway

AWS API Gateway can expose an HTTPS endpoint that communicates with your backend over HTTP. This way, your frontend can make secure HTTPS requests to the API Gateway, and the API Gateway handles communication with your backend over HTTP.

First, create API in AWS API Gateway. If the backend follows a RESTful pattern, select REST API

Now build your API branches in “Resources”: you can add resources under a path, and create a method for this resource. For the endpoint, enter the actual endpoint that is running on your EC2 instance. Also, enable CORS for each endpoint (both 4XX, 5XX, GET method) In my case, I need to click “enable CORS” and configure for twice until the CORS is actually enabled.

  • GET method with a query: besides assigning the query when creating this method, you also make other configurations. In Integrations request - URL query string parameters, add the mapping relation (see its info for detailed format)

  • POST or DELETE method with a request body: in the method request, find the “Request body” at the last row. Add a model with content type of “application/json” and Model of “empty”.

After all the configuration, deploy the APIs as one stage, and it’s all done, CONGRATULATIONS!