Next.js on EC2 with Express

Intermediate
⏱️ 15 min read
📚 Updated: Aug 2025
Next.js / Express / PM2

What you’ll learn

Next.js on Amazon EC2 lets you host full-stack React apps on your own Ubuntu server. This guide adds a thin Express layer that listens on port 80 and forwards requests to Next using next.getRequestHandler(), then runs everything under PM2.

You will scaffold the app locally, add index.js at the project root, upload without node_modules, install dependencies on the instance, build, and start.

Prerequisites

Before you begin:

AWS account and EC2
An AWS account with access to EC2.
Running EC2 instance
An EC2 instance (Ubuntu recommended) reachable over SSH.
Elastic IP (recommended)
An Elastic IP so your public address stays stable.
FileZilla
FileZilla (or another SFTP client) for uploads.
PuTTY or SSH
PuTTY or OpenSSH for shell access.
Node.js and npm
Node.js and npm installed on the instance.

Create the Next.js app locally

  1. Scaffold a new app (replace my-nextjs-app with your folder name):

    Terminal (local)
    npx create-next-app@latest my-nextjs-app

    Answer the prompts (TypeScript, ESLint, App Router, etc.) however you prefer. When it finishes:

    Terminal (local)
    cd my-nextjs-app
  2. Start the dev server to verify the template:

    Terminal (local)
    npm run dev

    Open http://localhost:3000/ in your browser.

    Default Next.js welcome page on localhost
    Browser
  3. Edit the starter home page in your editor:

    • App Router (default in recent templates): src/app/page.tsx or app/page.tsx
    • Pages Router (if you chose it): pages/index.tsx or pages/index.js

    There is no src/App folder in standard Next.js templates; the old path in some tutorials was incorrect.

  4. In the project root (next to package.json), add index.js for the custom server and install Express:

    Terminal (local)
    npm install express
    index.js
    const express = require('express');
    const next = require('next');
    
    const dev = process.env.NODE_ENV !== 'production';
    const app = next({ dev });
    const handle = app.getRequestHandler();
    
    app.prepare().then(() => {
      const server = express();
    
      server.get('*', (req, res) => {
        return handle(req, res);
      });
    
      const PORT = process.env.PORT || 80;
      server.listen(PORT, (err) => {
        if (err) throw err;
        console.log(`> Ready on http://localhost:${PORT}`);
      });
    });

    Commit your changes locally. You will upload the project without .git and without node_modules to save time.

Deploy on EC2

  1. Confirm the instance is running and note the public DNS or Elastic IP.

    EC2 instance running with Elastic IP
    Amazon EC2
  2. SSH in with PuTTY or ssh.

    PuTTY session to Ubuntu EC2
    PuTTY
  3. Verify Node and npm, then create a deploy directory owned by your user:

    Terminal (EC2)
    node -v && npm -v
    sudo mkdir -p /home/ubuntu/nextjs-website
    sudo chown ubuntu:ubuntu /home/ubuntu/nextjs-website
    Node and npm versions on Ubuntu
    Ubuntu
  4. For a quick FileZilla demo only, you may temporarily use wide permissions (tighten after upload):

    Terminal (EC2)
    sudo chmod 777 /home/ubuntu/nextjs-website
    nextjs-website folder permissions on EC2
    Ubuntu
  5. Upload the project with FileZilla. Exclude .git and node_modules (and usually .next so you rebuild on the server).

    FileZilla uploading Next.js project to EC2
    FileZilla
  6. Install PM2 globally:

    Terminal (EC2)
    sudo npm i -g pm2

    See PM2 for usage.

    Global PM2 install on Ubuntu
    Ubuntu
  7. Install dependencies and build for production (run inside the uploaded project folder):

    Terminal (EC2)
    cd /home/ubuntu/nextjs-website
    npm install
    NODE_ENV=production npm run build

    Use sudo npm install only if file ownership is wrong; prefer fixing ownership with chown instead of running npm as root long term.

  8. Start the custom server with PM2 (port 80 typically needs sudo):

    Terminal (EC2)
    cd /home/ubuntu/nextjs-website
    sudo pm2 start index.js

    Open inbound TCP 80 on the instance security group. You can disconnect SSH; PM2 keeps the process running.

    PM2 start index.js for Next.js on EC2
    Ubuntu
  9. Browse to http://<public-dns-or-ip>/. You should see your Next.js app.

    Next.js app in browser via EC2 public address
    Browser

    If you get errors, check sudo pm2 logs, confirm the build succeeded, and verify express appears in package.json dependencies.

Key takeaways

1

Use create-next-app locally, add Express + index.js, then upload and run npm install and npm run build on EC2.

2

Skip uploading node_modules; install on the server for the correct CPU/OS binaries.

3

PM2 supervises the Node process; add HTTPS and a reverse proxy when you move toward production.

Frequently asked questions

Express can listen on port 80 and pass requests to Next.js. Many teams instead run next start behind Nginx or a load balancer without a custom server file.
Generally no. Run npm install on the EC2 instance so dependencies match the server environment.
Yes for production mode. The build step compiles your application; skipping it usually breaks or degrades production behaviour.

Simpler Node hosting first?

If you only need a small API or static pages, an Express app on EC2 can be easier to operate than a full Next stack.

Express.js on EC2 →
Did you know?

A custom Node server (Express + next) is one way to host Next.js on a single VM. For many apps, next start behind Nginx or a load balancer is simpler. Check the Next.js custom server docs for trade-offs and version notes.

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful