DEV Community

Cover image for Deploy a Node.js Server on AWS EC2 with NGINX
HimanshuTamoli
HimanshuTamoli

Posted on

Deploy a Node.js Server on AWS EC2 with NGINX

If you have a Node.js application running locally, you can deploy it on an AWS EC2 server and make it accessible through the internet.

In this guide, we will:

  • Create an AWS EC2 instance
  • Connect to the server using SSH
  • Install Node.js
  • Run a Node.js application
  • Use PM2 to keep the application running
  • Install NGINX
  • Configure NGINX as a reverse proxy
  • Configure the AWS firewall
  • Access the Node.js application using the EC2 public IP

By the end, the architecture will look like this:

User
  |
  | HTTP Request
  ↓
NGINX :80
  |
  | Proxy
  ↓
Node.js :3000
Enter fullscreen mode Exit fullscreen mode

1. Create an EC2 Instance

Go to the AWS Console and open EC2.

Create a new instance.

For this tutorial, you can use:

AMI: Ubuntu
Instance type: t2.micro or t3.micro
Storage: 8 GB or more
Enter fullscreen mode Exit fullscreen mode

When creating the instance, make sure you create or select an SSH key pair.

Download the .pem file and keep it safe.

You will need it to connect to the server.

2. Configure the Security Group

The EC2 security group works like a firewall.

Add these inbound rules:

Type Port Source
SSH 22 My IP
HTTP 80 Anywhere
HTTPS 443 Anywhere

For testing, you can use 0.0.0.0/0 for HTTP.

For SSH, it is better to allow only your own IP instead of opening port 22 to everyone.

3. Connect to EC2 Using SSH

After the instance starts, copy its public IPv4 address.

It will look something like:

13.234.XX.XX
Enter fullscreen mode Exit fullscreen mode

Open your terminal and move to the directory containing your .pem file.

On Linux or macOS:

chmod 400 my-key.pem
Enter fullscreen mode Exit fullscreen mode

Then connect:

ssh -i my-key.pem ubuntu@YOUR_EC2_PUBLIC_IP
Enter fullscreen mode Exit fullscreen mode

Example:

ssh -i my-key.pem ubuntu@13.234.XX.XX
Enter fullscreen mode Exit fullscreen mode

If everything works, you are now inside your EC2 server.

4. Update the Server

First update the package list:

sudo apt update
Enter fullscreen mode Exit fullscreen mode

Then upgrade existing packages:

sudo apt upgrade -y
Enter fullscreen mode Exit fullscreen mode

5. Install Node.js

The easiest way is to use NodeSource.

First install curl:

sudo apt install curl -y
Enter fullscreen mode Exit fullscreen mode

Then install Node.js.

For example:

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
Enter fullscreen mode Exit fullscreen mode

Install Node.js:

sudo apt install nodejs -y
Enter fullscreen mode Exit fullscreen mode

Check the installation:

node -v
Enter fullscreen mode Exit fullscreen mode

And:

npm -v
Enter fullscreen mode Exit fullscreen mode

You should see the installed Node.js and npm versions.

6. Get Your Node.js Application

There are several ways to put your application on the EC2 server.

For example, if your project is on GitHub:

git clone https://github.com/USERNAME/REPOSITORY.git
Enter fullscreen mode Exit fullscreen mode

Enter the project:

cd REPOSITORY
Enter fullscreen mode Exit fullscreen mode

Install dependencies:

npm install
Enter fullscreen mode Exit fullscreen mode

If your application requires environment variables, create your .env file:

nano .env
Enter fullscreen mode Exit fullscreen mode

Add your environment variables and save the file.

7. Run the Node.js Server

Suppose your Node.js application contains:

const express = require("express");

const app = express();

app.get("/", (req, res) => {
  res.send("Hello from AWS EC2!");
});

app.listen(3000, () => {
  console.log("Server running on port 3000");
});
Enter fullscreen mode Exit fullscreen mode

Start it:

node server.js
Enter fullscreen mode Exit fullscreen mode

You should see:

Server running on port 3000
Enter fullscreen mode Exit fullscreen mode

Your application is now running on:

http://YOUR_EC2_PUBLIC_IP:3000
Enter fullscreen mode Exit fullscreen mode

However, we don't want users to access the application using port 3000.

This is where NGINX comes in.

8. Why Do We Need NGINX?

Instead of exposing your Node.js application directly, we can put NGINX in front of it.

The user sends a request to:

http://YOUR_EC2_PUBLIC_IP
Enter fullscreen mode Exit fullscreen mode

NGINX receives the request on port 80.

NGINX then forwards the request internally to:

http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

The flow becomes:

Browser
   |
   | :80
   ↓
NGINX
   |
   | :3000
   ↓
Node.js
Enter fullscreen mode Exit fullscreen mode

This is called a reverse proxy.

9. Install NGINX

Install NGINX:

sudo apt install nginx -y
Enter fullscreen mode Exit fullscreen mode

Check its status:

sudo systemctl status nginx
Enter fullscreen mode Exit fullscreen mode

If it is running, you should see something similar to:

Active: active (running)
Enter fullscreen mode Exit fullscreen mode

You can also enable NGINX to start automatically when the server boots:

sudo systemctl enable nginx
Enter fullscreen mode Exit fullscreen mode

10. Configure NGINX as a Reverse Proxy

NGINX configuration files are usually stored in:

/etc/nginx/
Enter fullscreen mode Exit fullscreen mode

For our application, create a new configuration:

sudo nano /etc/nginx/sites-available/nodeapp
Enter fullscreen mode Exit fullscreen mode

Add:

server {
    listen 80;

    server_name _;

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


    }
}
Enter fullscreen mode Exit fullscreen mode

Save the file.

11. Enable the Configuration

Create a symbolic link:

sudo ln -s /etc/nginx/sites-available/nodeapp /etc/nginx/sites-enabled/
Enter fullscreen mode Exit fullscreen mode

The default NGINX configuration can cause conflicts, so remove it:

sudo rm /etc/nginx/sites-enabled/default
Enter fullscreen mode Exit fullscreen mode

Before restarting NGINX, test the configuration:

sudo nginx -t
Enter fullscreen mode Exit fullscreen mode

You should see:

syntax is ok
test is successful
Enter fullscreen mode Exit fullscreen mode

Now restart NGINX:

sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode

12. Keep Node.js Running with PM2

There is one problem.

If you run:

node server.js
Enter fullscreen mode Exit fullscreen mode

and close your SSH connection, the application can stop.

We need a process manager.

Install PM2:

sudo npm install -g pm2
Enter fullscreen mode Exit fullscreen mode

Start your application:

pm2 start server.js --name nodeapp
Enter fullscreen mode Exit fullscreen mode

Check the process:

pm2 status
Enter fullscreen mode Exit fullscreen mode

You should see:

nodeapp    online
Enter fullscreen mode Exit fullscreen mode

Save the PM2 process list:

pm2 save
Enter fullscreen mode Exit fullscreen mode

Configure PM2 to start automatically after a server reboot:

pm2 startup
Enter fullscreen mode Exit fullscreen mode

PM2 will give you a command.

Copy and run that command.

Then run:

pm2 save
Enter fullscreen mode Exit fullscreen mode

Now your Node.js application can automatically restart after a server reboot.

13. Access Your Application

Open your browser and visit:

http://YOUR_EC2_PUBLIC_IP
Enter fullscreen mode Exit fullscreen mode

For example:

http://13.234.XX.XX
Enter fullscreen mode Exit fullscreen mode

The request flow is now:

Internet
    |
    ↓
AWS EC2
    |
    ↓
NGINX :80
    |
    ↓
Node.js :3000
Enter fullscreen mode Exit fullscreen mode

You don't need to expose port 3000 publicly.

NGINX communicates with Node.js internally through:

localhost:3000
Enter fullscreen mode Exit fullscreen mode

14. Important AWS Security Group Configuration

At this point, your EC2 security group should have something similar to:

SSH     22     Your IP
HTTP    80     0.0.0.0/0
HTTPS   443    0.0.0.0/0
Enter fullscreen mode Exit fullscreen mode

You do not need to expose port 3000 to the internet.

This is an important difference:

Public:

Internet → NGINX :80

Private:

NGINX → Node.js :3000
Enter fullscreen mode Exit fullscreen mode

15. Useful NGINX Commands

Check NGINX status:

sudo systemctl status nginx
Enter fullscreen mode Exit fullscreen mode

Start NGINX:

sudo systemctl start nginx
Enter fullscreen mode Exit fullscreen mode

Stop NGINX:

sudo systemctl stop nginx
Enter fullscreen mode Exit fullscreen mode

Restart NGINX:

sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode

Test configuration:

sudo nginx -t
Enter fullscreen mode Exit fullscreen mode

16. Useful PM2 Commands

Check applications:

pm2 status
Enter fullscreen mode Exit fullscreen mode

View logs:

pm2 logs
Enter fullscreen mode Exit fullscreen mode

Restart application:

pm2 restart nodeapp
Enter fullscreen mode Exit fullscreen mode

Stop application:

pm2 stop nodeapp
Enter fullscreen mode Exit fullscreen mode

Delete application:

pm2 delete nodeapp
Enter fullscreen mode Exit fullscreen mode

17. Common Problems

NGINX shows 502 Bad Gateway

A 502 Bad Gateway usually means NGINX cannot reach your Node.js application.

Check whether Node.js is running:

pm2 status
Enter fullscreen mode Exit fullscreen mode

You can also test the application directly from the EC2 server:

curl http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

If this doesn't return your application response, fix the Node.js application first.

NGINX configuration error

Run:

sudo nginx -t
Enter fullscreen mode Exit fullscreen mode

It will tell you where the configuration problem is.

Cannot access the EC2 IP

Check your AWS security group.

Make sure port 80 is allowed.

Also check NGINX:

sudo systemctl status nginx
Enter fullscreen mode Exit fullscreen mode

Node.js application stopped

Check PM2:

pm2 status
Enter fullscreen mode Exit fullscreen mode

Then check logs:

pm2 logs nodeapp
Enter fullscreen mode Exit fullscreen mode

18. Final Architecture

After completing everything, your deployment looks like this:

                   INTERNET
                       |
                       |
                 Public IP :80
                       |
                       ↓
                  ┌─────────┐
                  │  NGINX  │
                  │  :80    │
                  └────┬────┘
                       |
                       | Reverse Proxy
                       ↓
                  ┌─────────┐
                  │ Node.js │
                  │  :3000  │
                  └─────────┘
                       |
                       ↓
                    Your App
Enter fullscreen mode Exit fullscreen mode

The important idea is simple:

NGINX handles incoming HTTP requests, while Node.js handles your application logic.

You can later add a domain name and HTTPS on top of this setup using Let's Encrypt and Certbot.

Conclusion

You now have a Node.js application running on an AWS EC2 server with NGINX acting as a reverse proxy.

The basic production flow is:

User
 ↓
Domain / Public IP
 ↓
NGINX
 ↓
Node.js
 ↓
Database / External Services
Enter fullscreen mode Exit fullscreen mode

This setup is a good foundation for learning real-world server deployment before moving to more advanced tools such as Docker, load balancers, CI/CD, and Kubernetes.

Want the Video Walkthrough?

If you prefer following the process visually, I have also explained the deployment in more detail in this
video: https://www.youtube.com/watch?v=4cgvi_uCKiA

Watch the detailed AWS EC2 + NGINX + Node.js deployment video

You can follow this article alongside the video and execute each command directly on your EC2 instance.

Top comments (0)