DEV Community

kuldeepg3
kuldeepg3

Posted on

🚀 Deploying a Full-Stack App (Next.js + Express + MongoDB) on a Single AWS EC2 Instance

Example Project: ProjectX

Stack: Next.js (frontend) · Express.js (backend) · MongoDB (Atlas or self-hosted)

Deployment Tools: Nginx, PM2, Let’s Encrypt (Certbot)

Target Domains:

  • dashboard.projectname.com → Frontend
  • api.projectname.com → Backend

Difficulty: Intermediate

Best For: Developers deploying small to medium full-stack apps on AWS


🧭 Meta Description

A complete step-by-step guide to deploying a Next.js + Express + MongoDB app on a single AWS EC2 instance using Nginx, PM2, and Let’s Encrypt SSL — a production-ready setup for small projects.


⚙️ Architecture Overview

We’ll host both frontend and backend on one t3.small EC2 instance running Ubuntu 22.04 LTS.

  • Nginx → Reverse proxy + SSL termination
  • PM2 → Node process manager
  • Next.js → Served via Nginx at dashboard.projectname.com
  • Express API → Proxied internally on port 3000 via api.projectname.com
  • MongoDB → Hosted on MongoDB Atlas (recommended) or locally (for dev/staging)

🔑 Pre-Deployment Requirements

Before deployment, gather:

  • AWS access (EC2 + Security Groups + Elastic IP)
  • Domain DNS access (to point A records)
  • MongoDB URI (Atlas or local credentials)
  • SSL email (for Let’s Encrypt)
  • SSH key for server access
  • Environment variable secrets (JWT_SECRET, DB_URI, etc.)

☁️ Step-by-Step EC2 Setup

1️⃣ Provision & Update Your Server

sudo apt update && sudo apt upgrade -y
sudo apt install -y build-essential git curl nginx certbot python3-certbot-nginx ufw

Enter fullscreen mode Exit fullscreen mode

2️⃣ Install Node.js & PM2

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
sudo npm install -g pm2
pm2 startup systemd --hp /home/ubuntu

Enter fullscreen mode Exit fullscreen mode

3️⃣ Prepare Project Directories

sudo mkdir -p /var/www/projectx/{frontend,backend}
sudo chown -R ubuntu:ubuntu /var/www/projectx
cd /var/www/projectx

Enter fullscreen mode Exit fullscreen mode

4️⃣ Clone Your Repositories

cd frontend && git clone <FRONTEND_REPO_URL> .
cd ../backend && git clone <BACKEND_REPO_URL> .

Enter fullscreen mode Exit fullscreen mode

5️⃣ Install Dependencies & Build

# Frontend
cd /var/www/projectx/frontend
npm ci && npm run build

# Backend
cd /var/www/projectx/backend
npm ci

Enter fullscreen mode Exit fullscreen mode

⚙️ Managing Processes with PM2

Create /var/www/projectx/ecosystem.config.js:

module.exports = {
  apps: [
    {
      name: "projectx-backend",
      cwd: "/var/www/projectx/backend",
      script: "npm",
      args: "start",
      env: {
        NODE_ENV: "production",
        PORT: 3000,
        MONGODB_URI: "your_mongodb_uri",
        JWT_SECRET: "your_secret",
      },
    },
    {
      name: "projectx-frontend",
      cwd: "/var/www/projectx/frontend",
      script: "npm",
      args: "run start",
      env: {
        NODE_ENV: "production",
        PORT: 4000,
        NEXT_PUBLIC_API_BASE: "https://api.projectname.com",
      },
    },
  ],
};

Enter fullscreen mode Exit fullscreen mode

Start both:

pm2 start ecosystem.config.js
pm2 save

Enter fullscreen mode Exit fullscreen mode

🌐 Configure Nginx Reverse Proxy

Create two files under /etc/nginx/sites-available/ and link them.

dashboard.projectname.com

server {
  listen 80;
  server_name dashboard.projectname.com;
  return 301 https://$host$request_uri;
}

Enter fullscreen mode Exit fullscreen mode

api.projectname.com

server {
  listen 80;
  server_name api.projectname.com;
  return 301 https://$host$request_uri;
}

Enter fullscreen mode Exit fullscreen mode

After SSL setup, Certbot will inject HTTPS blocks automatically, but if you prefer manual configuration:

server {
  listen 443 ssl;
  server_name api.projectname.com;

  ssl_certificate /etc/letsencrypt/live/api.projectname.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/api.projectname.com/privkey.pem;

  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

Enter fullscreen mode Exit fullscreen mode

Enable and reload Nginx:

sudo ln -s /etc/nginx/sites-available/api.projectname.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/dashboard.projectname.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Enter fullscreen mode Exit fullscreen mode

🔐 Enable HTTPS with Let’s Encrypt

sudo certbot --nginx -d dashboard.projectname.com -d api.projectname.com \
--agree-tos --redirect --no-eff-email -m admin@projectname.com
sudo certbot renew --dry-run

Enter fullscreen mode Exit fullscreen mode

🧱 Security & Firewall

AWS Security Group

Port Service Access
22 SSH Admin IPs only
80 HTTP 0.0.0.0/0
443 HTTPS 0.0.0.0/0
27017 MongoDB Localhost or private IPs only

UFW on EC2

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Enter fullscreen mode Exit fullscreen mode

🗄️ MongoDB — Use Atlas for Production

Self-hosting MongoDB on the same instance is possible but not ideal.

Atlas offers backups, scaling, and security with less maintenance.

If you must self-host:

  • Bind to 127.0.0.1
  • Enable authentication
  • Automate daily backups (mongodump → S3)

🔄 Deployment Automation Script

Example /usr/local/bin/deploy_projectx.sh:

#!/usr/bin/env bash
set -e
APP_DIR="/var/www/projectx"

cd $APP_DIR/backend
git pull origin main
npm ci
pm2 restart projectx-backend

cd $APP_DIR/frontend
git pull origin main
npm ci && npm run build
pm2 restart projectx-frontend

sudo nginx -t && sudo systemctl reload nginx

Enter fullscreen mode Exit fullscreen mode

🧠 Monitoring, Backups & Maintenance

  • Monitoring: CloudWatch, Datadog, or PM2 Keymetrics
  • Backups:
    • Database → Atlas or mongodump
    • Config → secure Git/S3
  • Log Rotation:

    pm2 install pm2-logrotate
    pm2 set pm2-logrotate:max_size 10M
    
    

⚡ Scaling & Future Improvements

Area Recommendation
DB Move to MongoDB Atlas
Infra Separate frontend/backend instances
CI/CD Use GitHub Actions or AWS CodeDeploy
Scaling Add ALB + Auto Scaling Group
Security Add WAF & rate limiting
Secrets Use AWS Secrets Manager

🧾 Final Handover Checklist

✅ Elastic IP + DNS records set

✅ SSL certificates issued & tested

✅ PM2 configured to auto-start

✅ Logs & backups working

✅ Security rules locked down

✅ Credentials documented securely


✍️ Wrapping Up

Hosting your full-stack Next.js + Express app on a single EC2 instance is efficient for small projects and prototypes.

With PM2, Nginx, and Certbot, you get a production-grade deployment without Kubernetes or ECS complexity.

As your app grows, migrate MongoDB to Atlas, host the frontend on Vercel, and implement CI/CD workflows for automated deployments.


—all generic and reusable for your own stack.

Top comments (0)