Introduction:
Deploy Node.js App on AWS EC2 (Elastic Compute Cloud) is a service provided by Amazon Web Services that allows users to rent virtual servers (instances) to run applications on demand. It provides flexibility, scalability, and cost-effectiveness, making it an excellent choice for hosting Node.js applications. Node.js is an event-driven, non-blocking I/O model that is ideal for building scalable network applications.
Node.js, on the other hand, is an asynchronous, event‑driven JavaScript runtime designed to build scalable network applications. It handles many concurrent connections through an event loop without spawning operating‑system threads or locking resources; almost no function performs I/O directly, so the process rarely blocks. Because nothing blocks, “scalable systems are very reasonable to develop in Node.js, and HTTP support is built in with streaming and low‑latency communication in mind.
This guide will walk you through the process of deploying a Node.js app on an AWS EC2 instance. It’s aimed at developers and system administrators who want to deploy their Node.js applications on AWS EC2 instances for production environments.
Also read our other blog: A Step-by-Step Guide to Deploying Mattermost on AWS
Why Choose AWS EC2 for Node.js Deployment?
We choose AWS EC2 for Node.js Deployment because of the following reasons:
- Scalability: EC2 instances can be easily scaled up or down based on demand, ensuring your application can handle varying loads efficiently.
- Flexibility: EC2 offers a wide range of instance types optimized for different use cases, from general-purpose to memory-optimized and compute-optimized instances.
- Integration: Seamlessly integrates with other AWS services, such as RDS for databases, S3 for storage, and CloudFront for content delivery, providing a comprehensive infrastructure for your application.
Benefits of Deploying Node.js Application on AWS EC2
The benefits of deploying Node.js App on AWS EC2 are as under:
- Wide Variety of Instance Types – EC2 offers the broadest and deepest selection of instances, including general purpose, compute optimized, memory optimized, storage optimized, and accelerated computing options. This helps match your Node.js workload with the most suitable compute, memory, storage, and networking balance.
- Choice of Powerful Processors – Instances are powered by processors from Intel, AMD, NVIDIA, and AWS, providing flexibility to choose based on performance needs and cost optimization.
- Performance Optimization for Specific Workloads – Local storage and enhanced networking capabilities help optimize performance for workloads that are disk-intensive or network I/O bound.
- Bare Metal Options – Many instance types include bare metal configurations, giving applications direct access to the underlying server’s processor and memory. This is useful for non-virtualized environments or when you want to use your own hypervisor.
- Tailored Resource Selection – The AWS Compute Optimizer tool can recommend the most suitable instance type for your Node.js application, reducing costs and improving performance.
- Scalability and Flexibility – With such a wide range of options, you can start small and scale up as your Node.js app’s traffic and performance requirements grow.
- Amazon Machine Images (AMIs): Preconfigured templates for your instances that package the components you need for your server (including the operating system and additional software).
- Amazon EBS volumes: Persistent storage volumes for your data using Amazon Elastic Block Store (Amazon EBS).
- Instance store volumes: Storage volumes for temporary data that is deleted when you stop, hibernate, or terminate your instance.
- Key pairs: Secure login information for your instances. AWS stores the public key and you store the private key in a secure place.
- Security groups: A virtual firewall that allows you to specify the protocols, ports, and source IP ranges that can reach your instances, and the destination IP ranges to which your instances can connect.
Steps of Deploying Node.js Application on AWS EC2:
The Steps of deploying a Node.js App on AWS EC2 are as under:
Step 1: Setting Up AWS EC2 Instance
The first step in deploying your Node.js app is setting up an EC2 instance. Here’s how to do it:
- Choose the right AMI and instance type:
- Configure instance details
- Add storage
- Set up a security group
- Create a key pair
After configuring your instance, click Launch. You can now connect to the EC2 instance via SSH.
Step 2: Securely Accessing Your EC2 Instance
To access your EC2 instance, follow these steps:
- Connect via SSH:
- Open your terminal and run the following command to connect to the EC2 instance:
chmod 400 your-key.pem
ssh -i your-key.pem ec2-user@your-ec2-public-ip # Amazon Linux
Or for Ubuntu:
chmod 400 your-key.pem
ssh -i your-key.pem ubuntu@your-ec2-public-ip # Ubuntu
- Update system packages: Once connected, update the system packages to ensure your instance is up-to-date.
# For Amazon Linux
sudo yum update -y
# For Ubuntu
sudo apt update && sudo apt upgrade -y
Step 3: Installing Node.js and NPM
Now, let’s install Node.js and NPM (Node Package Manager).
Installing Node.js & npm on Linux
Method: Using NodeSource Repository
(Works for Ubuntu/Debian-based distributions — for other distros, adjust accordingly.)
- Update your package index:
sudo apt update
sudo apt upgrade -y
2. Install curl if not already installed:
sudo apt install curl -y
3. Add NodeSource repository for the LTS version:
curl -fsSL <https://deb.nodesource.com/setup_lts.x> | sudo -E bash -
4. Install Node.js (includes npm):
sudo apt install -y nodejs
5. Verify installation:
node -v
npm -v
6. Install Node Version Manager (nvm):
curl -o- <https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh> | bash
source ~/.bashrc
Now you need to activate nvm by using the command :
#. ~/.nvm/nvm.sh
7. Install Node.js using nvm:
nvm install --lts
nvm use --lts
node -v
npm -v
nvm allows you to install and manage different versions of Node.js, which is useful for managing dependencies in different environments.
Step 4: Preparing Your Node.js Application For AWS EC2
1. Cloning the Repository from GitHub
If your Node.js app’s source code is stored in a GitHub repository, you’ll first need to download it onto your EC2 instance.
- Install Git (if not already installed)
sudo apt install git -y # For Ubuntu/Debian
sudo yum install git -y # For Amazon Linux/CentOS
2. Clone your repository
Replace the URL with your own repository’s HTTPS or SSH link:
git clone <https://github.com/your-username/your-repo-name.git>
3. Move into the project directory
cd your-repo-name
2. Installing Dependencies
After cloning your application, you’ll need to install its required packages using npm (which comes with Node.js).
- Run npm install
This will read thepackage.json
file in your project and install all listed dependencies:
npm install
- Verify installation Check that the
node_modules
folder is created and that there are no major errors in the output. - (Optional) Environment Variables If your app requires environment variables (e.g.,
.env
file), create or upload it to the project directory:
nano .env
Add your environment keys and values, then save.
3. Testing the Application Locally
Before moving on to production setup:
node app.js
or if your entry file is different:
node server.js
Check in your browser using:
<http://your-ec2-public-ip:3000>
(Replace 3000
with the port your app uses.)
Step 5: Reverse Proxy Setup with Nginx
1. Purpose of a Reverse Proxy
A reverse proxy acts as an intermediary between clients (e.g., browsers) and your Node.js application.
- Why use it?
- It allows you to serve your app on standard web ports (80 for HTTP, 443 for HTTPS).
- It hides the actual Node.js process/port from public exposure.
- It can handle SSL/TLS termination, caching, and load balancing.
2. Installing Nginx
Now, let’s set up Nginx as a reverse proxy for your Node.js application. This allows you to handle incoming traffic on HTTP/HTTPS ports and forward it to your Node.js application.
Ubuntu/Debian
sudo apt update
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
Amazon Linux / RHEL / CentOS
sudo yum install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
Security group note:
In your AWS EC2 console, ensure your instance’s Security Group allows inbound traffic on:
- Port 80 (HTTP)
- Port 443 (HTTPS, for SSL)
3. Configuring Nginx as a Reverse Proxy
Edit the /etc/nginx/nginx.conf
or create a new config file under /etc/nginx/sites-available
:
a) Create a Server Block
You’ll configure Nginx to listen on port 80 and forward requests to your Node.js app running locally (e.g., on port 3000).
Ubuntu/Debian (sites-available / sites-enabled method)
sudo nano /etc/nginx/sites-available/yourdomain.com
Paste:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass <http://127.0.0.1:3000>;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Enable the config:
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default # optional, to remove default site
Amazon Linux / CentOS (conf.d method)
sudo nano /etc/nginx/conf.d/yourdomain.conf
Paste the same server { … }
block above.
4. Test and Reload Nginx
Check configuration syntax:
sudo nginx -t
Reload to apply changes:
sudo systemctl reload nginx
5. Run Node.js App on Localhost
Your Node.js app should listen only on localhost for security:
app.listen(3000, '127.0.0.1', () => {
console.log('App running on http://127.0.0.1:3000');
});
6. Verify the Setup
- Visit
http://yourdomain.com
(or your EC2 public IP if no domain yet). - If your app loads, the reverse proxy is working.
- If not, check:
- Nginx error logs:
/var/log/nginx/error.log
- Node.js process logs
- Nginx error logs:
Step 6: Attaching a Domain and Setting Up SSL
1. Point Your Domain to the EC2 Instance IP
a) Get your EC2 Public IP
- In the AWS EC2 console, select your instance and find the IPv4 Public IP in the details panel.
b) Update DNS Records
- Log into your domain registrar’s dashboard (GoDaddy, Namecheap, Cloudflare, etc.).
- Create/modify A records:
@
(root domain) → point to your EC2 public IP.www
→ point to the same IP (optional).
- Save changes.
c) Allow Time for Propagation
- DNS updates can take a few minutes to a few hours.
- Check with:
nslookup yourdomain.com
2. Obtain an SSL Certificate with Let’s Encrypt (Certbot)
We’ll use Certbot to get a free SSL/TLS certificate.
a) Install Certbot and Nginx Plugin
Ubuntu/Debian
sudo apt update
sudo apt install -y snapd
sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
Amazon Linux / RHEL / CentOS
sudo yum install -y snapd
sudo systemctl enable --now snapd.socket
sudo ln -s /var/lib/snapd/snap /snap
sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
b) Get the Certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
- Certbot will verify domain ownership, issue the certificate, and update your Nginx config.
3. Configure Nginx for SSL
If you used the --nginx
option, Certbot auto-configures Nginx.
Your secure server block will look like this:
server {
listen 443 ssl;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass <http://127.0.0.1:3000>;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
This forces all HTTP traffic to redirect to HTTPS.
4. Test and Set Up Auto-Renewal
Test certificate:
- Visit
https://yourdomain.com
— look for the secure lock icon.
Dry-run renewal check:
sudo certbot renew --dry-run
Let’s Encrypt certificates are valid for 90 days. Certbot will install a cron job or systemd timer to auto-renew them.
Step 7: Managing Node.js Process with PM2
1. Installing PM2
PM2 is a process manager for Node.js applications. It keeps your app running in the background, restarts it if it crashes, and can auto-start it when the server reboots.
Install PM2 globally with npm:
sudo npm install -g pm2
Verify installation:
pm2 -v
2. Starting Your Node.js App with PM2
Navigate to your project directory (where your main file like app.js
or server.js
is located), then start the app with PM2:
cd /home/ec2-user/your-node-app
pm2 start app.js --name "my-node-app"
app.js
→ replace with your main entry file.-name
→ gives your app a readable name for easier management.
Common commands:
pm2 list
→ shows all running processes.pm2 status
→ check status of your apps.pm2 logs my-node-app
→ view real-time logs.pm2 stop my-node-app
→ stop the app.pm2 restart my-node-app
→ restart the app.
3. Setting Up PM2 to Auto-Start on System Reboot
By default, if your EC2 instance reboots, the Node.js app will stop running. PM2 can configure a startup script to solve this.
Run the following command:
pm2 startup systemd
This will output a command tailored to your system. Copy and run it (it usually looks like this):
sudo env PATH=$PATH:/home/ec2-user/.nvm/versions/node/$(node -v)/bin pm2 startup systemd -u ec2-user --hp /home/ec2-user
Now save your current PM2 process list so it restarts automatically after reboot:
pm2 save
Test by rebooting your instance:
sudo reboot
After logging back in, check with:
pm2 list
Your app should still be running.
This ensures your Node.js application stays alive 24/7 and recovers automatically from crashes or reboots.
Step 8: Firewall and Security
1. Ensuring EC2 Security Group Settings
Security Groups are AWS-level firewalls applied to your EC2 instance.
- Go to the AWS EC2 Console → Instances → Select your instance → Security tab.
- Edit Inbound Rules to allow only necessary ports:
- 22 (SSH): Restrict access to your own IP (do not keep it open to
0.0.0.0/0
). - 80 (HTTP): Open to all if you serve web traffic.
- 443 (HTTPS): Open to all if using SSL.
- 22 (SSH): Restrict access to your own IP (do not keep it open to
- Remove any other open ports unless explicitly needed.
This minimizes exposure to unwanted connections.
2. Configuring OS Firewall (Linux)
Besides AWS security groups, you can enable the instance’s own firewall for an extra layer of defense.
Ubuntu/Debian (UFW)
- Install and enable UFW:
sudo apt install ufw -y
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full' # allows ports 80 and 443
sudo ufw enable
sudo ufw status
Amazon Linux / CentOS (firewalld)
- Enable and configure firewalld:
sudo systemctl start firewalld
sudo systemctl enable firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
3. Additional Security Best Practices
- Disable root login via SSH: Edit
/etc/ssh/sshd_config
and set:
PermitRootLogin no
Then restart SSH:
sudo systemctl restart sshd
- Use SSH key authentication only: Avoid password-based logins. Ensure
PasswordAuthentication no is set in sshd_config. - Keep software updated:
# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y
# Amazon Linux
sudo yum update -y
- Fail2ban (optional): Install Fail2ban to block repeated failed login attempts.
sudo apt install fail2ban -y # Ubuntu/Debian
Step 9: Monitoring and Logs
1. Setting Up Basic Monitoring with CloudWatch (Optional)
Amazon CloudWatch lets you track performance metrics and system health.
a) Enable CloudWatch Agent
- Install the CloudWatch Agent:
- Amazon Linux / CentOS:
sudo yum install -y amazon-cloudwatch-agent
- Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y amazon-cloudwatch-agent
2. Configure the agent interactively:
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
- Choose metrics you want to collect (CPU, memory, disk, network).
- Save the configuration.
3. Start the agent:
sudo systemctl enable amazon-cloudwatch-agent
sudo systemctl start amazon-cloudwatch-agent
4. Check metrics in the AWS CloudWatch Console under EC2 > Metrics.
This helps you detect CPU spikes, memory leaks, or abnormal traffic patterns before they crash your app.
2. Accessing and Interpreting Application Logs with PM2
Since your Node.js app is running under PM2, you can use its built-in log management.
a) Viewing logs in real-time
pm2 logs my-node-app
- Displays both stdout (normal logs) and stderr (error logs).
- Useful for debugging crashes or API errors.
b) Checking only errors
pm2 logs my-node-app --lines 100 --err
c) Log file locations
PM2 stores logs in files inside:
~/.pm2/logs/
You’ll find two files per app:
my-node-app-out.log
(standard output)my-node-app-error.log
(errors only)
d) Monitoring performance with PM2
pm2 monit
- Displays CPU and memory usage of each app in a live dashboard.
3. Best Practices for Monitoring & Logs
- Rotate logs: Large logs can fill disk space. Use
pm2 logrotate
module:
pm2 install pm2-logrotate
- Send logs to CloudWatch: You can configure the CloudWatch Agent to also stream PM2 logs into AWS CloudWatch for centralized viewing.
Conclusion
Deploying a Node.js application on AWS EC2 may seem like a long journey at first, but by breaking it into clear steps, the process becomes both manageable and rewarding. Hosting Node.js on AWS gives you full control over your environment while providing scalability and flexibility. With EC2 and Node.js’s event-driven architecture, you can handle high concurrency and deliver a performant app to your users. By following this step-by-step guide, you’ve set up a robust, production-ready application with reverse proxy, SSL, and process management using PM2.
In short, you’ve built the foundation for a robust deployment workflow—one that empowers you to deliver your Node.js applications to users across the globe with speed, stability, and confidence.
Explore other blogs for better understanding: Deploying NodeJS APP on AWS EC2 Instance — Step by Step