How to Deploy a Web App on AWS EC2: A Beginner’s Step-by-Step Guide

Introduction

If you’re looking to deploy a web app on AWS for the first time, EC2 is one of the most flexible and educational places to start. Unlike fully managed services such as Elastic Beanstalk, Amplify, or App Runner, EC2 gives you full control over your server, which is perfect for learning how things actually work under the hood.

In this guide, we’ll walk through the entire process step by step: launching an EC2 instance, configuring security groups, installing Node.js, and deploying a simple web app using PM2 as a process manager and Nginx as a reverse proxy. By the end, you’ll have a real, running web application accessible from the internet.

aws ec2 server

Why Choose EC2 to Deploy Your Web App?

AWS offers several ways to host web applications. Here’s a quick comparison to help you understand where EC2 fits in:

Service Best For Control Level
EC2 Full-stack apps, custom configs, learning Full
Elastic Beanstalk Managed deployments with less config Medium
Amplify Static sites and SPAs Low
App Runner Containerized apps from a repo Low

EC2 is the winner when you want to truly understand deployment, run background workers, or customize your stack.

Prerequisites

  • An active AWS account (Free Tier eligible works fine)
  • Basic familiarity with the terminal / SSH
  • A simple Node.js app to deploy (we’ll create one below)
  • An SSH client (Terminal on macOS/Linux, or PowerShell/PuTTY on Windows)

Step 1: Launch an EC2 Instance

  1. Log in to the AWS Management Console and search for EC2.
  2. Click Launch Instance.
  3. Give your instance a name (e.g. my-webapp-server).
  4. Choose an AMI: select Ubuntu Server 24.04 LTS (free tier eligible).
  5. Instance type: pick t2.micro or t3.micro (Free Tier).
  6. Under Key pair, click Create new key pair. Name it, select RSA and .pem format, then download it. Store it safely, you’ll need it to connect.
  7. Leave storage at default (8 GB gp3 is plenty).
  8. Click Launch Instance.

Common pitfall: If you lose your .pem key file, you cannot recover it. AWS won’t regenerate it. Back it up immediately.

aws ec2 server

Step 2: Configure Security Groups

Security Groups are virtual firewalls. To make your web app reachable, you need to open the right ports.

  1. In the EC2 console, go to Security Groups.
  2. Select the security group attached to your instance.
  3. Click Edit inbound rules and add:
Type Port Source Purpose
SSH 22 My IP Remote access
HTTP 80 Anywhere (0.0.0.0/0) Public web traffic
HTTPS 443 Anywhere (0.0.0.0/0) Secure traffic (later with SSL)

Pitfall to avoid: Never open port 22 to 0.0.0.0/0 in production. Always restrict SSH to your own IP.

Step 3: Connect to Your EC2 Instance via SSH

Open your terminal and navigate to where your .pem key is stored:

chmod 400 my-key.pem
ssh -i "my-key.pem" ubuntu@your-ec2-public-ip

You’ll find your public IP in the EC2 dashboard under Instance details. A fuller account is out there.

Common pitfall: If you get a permissions error on the key file, run chmod 400 on it. If the connection times out, double-check your security group’s SSH rule.

Step 4: Install Node.js on the Server

Once connected, update the system and install Node.js (LTS version):

sudo apt update && sudo apt upgrade -y
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
node -v
npm -v

You should now see Node.js and npm versions printed in the terminal.

aws ec2 server

Step 5: Deploy a Simple Web App

Let’s create a minimal Express app for demonstration:

mkdir ~/myapp && cd ~/myapp
npm init -y
npm install express

Create a file called index.js:

nano index.js

Paste this code:

const express = require('express');
const app = express();
const PORT = 3000;

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

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

Test it:

node index.js

You should see the confirmation message. Stop it with Ctrl + C.

Tip: In real projects, you’d pull your code from GitHub using git clone. Anyone digging further should read Create new web app on AWS.

Step 6: Keep the App Running with PM2

If we close the SSH session, our app dies. That’s where PM2 comes in, keeping Node processes alive and restarting them on crash or reboot.

sudo npm install -g pm2
pm2 start index.js --name myapp
pm2 save
pm2 startup

Run the command PM2 prints out (it configures auto-start on reboot).

Useful commands:

  • pm2 list shows running apps
  • pm2 logs streams logs
  • pm2 restart myapp restarts your app

Step 7: Configure Nginx as a Reverse Proxy

Your app is running on port 3000, but users expect port 80 (HTTP). Nginx will forward traffic from port 80 to your Node app.

sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

Now edit the default site configuration:

sudo nano /etc/nginx/sites-available/default

Replace the server block with:

server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://localhost: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;
    }
}

Test and restart Nginx:

sudo nginx -t
sudo systemctl restart nginx

Now open your browser and visit http://your-ec2-public-ip. Your app should be live!

aws ec2 server

Step 8 (Bonus): Add a Domain and SSL

Once your app is running:

  1. Point your domain’s A record to your EC2 public IP.
  2. Install Certbot for free SSL from Let’s Encrypt:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot will automatically update your Nginx config with HTTPS.

Pro tip: Consider using an Elastic IP so your public IP doesn’t change if the instance restarts.

Common Pitfalls Recap

  • Can’t access site: 90% of the time it’s a security group issue. Confirm ports 80/443 are open.
  • App works locally on server but not from browser: Nginx isn’t forwarding properly, run sudo nginx -t to check config.
  • SSH “Permission denied”: Wrong user (use ubuntu for Ubuntu AMIs, ec2-user for Amazon Linux).
  • App stops after logout: Forgot PM2, always use a process manager.
  • Instance stopped and IP changed: Attach an Elastic IP for stability.

Final Thoughts

You now have a fully functional web app running on AWS EC2, managed by PM2 and served through Nginx. This setup is production-grade for small to medium projects and gives you a solid foundation before exploring more advanced deployment methods like Docker, ECS, or Kubernetes.

Learning to deploy a web app on AWS using EC2 pays off long-term, you understand every layer of the stack, and troubleshoot faster when things go wrong.

FAQ

Is deploying a web app on AWS EC2 free?

Yes, if you stay within the AWS Free Tier: a t2.micro or t3.micro instance running 750 hours/month for the first 12 months is free. You may incur small charges for data transfer or Elastic IPs when the instance is stopped.

Should I use EC2 or Elastic Beanstalk?

Use EC2 if you want control and learning. Use Elastic Beanstalk if you want managed scaling and deployments with less configuration.

Do I need Nginx if I’m using Node.js?

Technically no, but Nginx is highly recommended. It handles SSL, static files, load balancing, and security more efficiently than running Node.js directly on port 80.

What’s the difference between PM2 and systemd?

Both keep processes running, but PM2 is purpose-built for Node.js with clustering, log management, and easy monitoring, making it more developer-friendly.

How do I deploy updates to my app?

SSH into your server, pull your latest code (usually with git pull), install dependencies, and run pm2 restart myapp. For automation, consider using GitHub Actions or AWS CodeDeploy. dev.to has covered this at length.