Deploy a React app on EC2

Intermediate
⏱️ 13 min read
📚 Updated: Aug 2025
React / CRA / PM2

What you’ll learn

Deploy a React production build on Ubuntu EC2 using Create React App locally, a small Express server to serve the build/ folder (including client-side routes), FileZilla for upload, and PM2 to keep the server running.

The sample listens on port 3000; open that port in your EC2 security group (or change the port in code and in the group).

Prerequisites

AWS account and EC2
An AWS account with access to EC2.
Running EC2 instance
An EC2 instance (Ubuntu recommended).
Elastic IP (recommended)
An Elastic IP for a stable public address.
FileZilla
FileZilla (SFTP) configured for the instance.
PuTTY or SSH
PuTTY or OpenSSH.
Node.js and npm
Node.js and npm on the instance.

Create the React app locally

  1. Scaffold an app (replace my-react-app with your name):

    Terminal (local)
    npx create-react-app@latest my-react-app
    Terminal (local)
    cd my-react-app
  2. Start the dev server:

    Terminal (local)
    npm start

    The app opens at http://localhost:3000/.

    Default Create React App running on localhost
    Browser
  3. Edit the UI under src/ — commonly src/App.js or src/App.jsx depending on your template.

Deploy on EC2

Add index.js at the project root (next to package.json) before you upload. It serves the CRA build folder and falls back to index.html for client-side routes.

index.js (project root)
const express = require('express');
const path = require('path');

const app = express();
const port = process.env.PORT || 3000;

app.use(express.static(path.join(__dirname, 'build')));

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});
  1. Confirm the instance is running; note the public DNS or IP.

    EC2 instance running
    Amazon EC2
  2. Connect with PuTTY or SSH.

    PuTTY session to EC2
    PuTTY
  3. Verify Node and npm.

    Node and npm versions
    Ubuntu
  4. Create a deploy directory and set ownership:

    Terminal (EC2)
    sudo mkdir -p /home/ubuntu/reactjs-website
    sudo chown ubuntu:ubuntu /home/ubuntu/reactjs-website
    Creating reactjs-website directory on EC2
    Ubuntu
  5. Optional demo-only chmod 777 for SFTP (tighten later):

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

    FileZilla uploading React project to EC2
    FileZilla
  7. Install PM2 globally:

    Terminal (EC2)
    sudo npm i -g pm2

    PM2

    PM2 global install
    Ubuntu
  8. From the project directory on EC2, install dependencies, add Express, and build:

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

    Avoid habitual sudo npm install; fix file ownership with chown if uploads land as the wrong user.

  9. Start with PM2:

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

    Allow inbound TCP 3000 (or your chosen PORT) in the security group. Visit http://<public-ip>:3000/.

    PM2 starting Express server for React build
    Ubuntu
  10. You should see the built React app in the browser.

    React app served from EC2
    Browser

Key takeaways

1

npm run build produces static files in build/; Express (or Nginx) serves them in production.

2

A catch-all GET * route returns index.html so React Router paths work on refresh.

3

PM2 keeps the server alive; open the matching port in the security group.

Frequently asked questions

Express is a simple way to serve static files and return index.html for unknown paths. Nginx, Caddy, or S3 plus CloudFront are common alternatives.
The sample uses 3000 to match local CRA defaults. Match your security group to whatever port you configure in code or environment variables.
Typically no. Run npm install on EC2 so dependencies compile for that OS and CPU.

Next: Next.js on EC2

When you outgrow a static React bundle, Next.js adds routing, SSR, and API routes in one framework.

Next.js on EC2 →
Did you know?

Create React App is in maintenance mode; new projects often use Vite or a framework. This guide still matches a classic CRA build/ output folder. If you use Vite, the output is usually dist/ and you must point Express at that directory instead.

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.

7 people found this page helpful