This content originally appeared on DEV Community and was authored by Suvrajeet Banerjee
High Level OverView: A practical, story-driven guide to take a React SPA on a live Ubuntu EC2 Instance using Nginx — repeatable and beginner-friendly.
Table of contents
Short version — Summary / Overview
Why this guide exists — a quick story
Prerequisites
Step 1 — Launch Ubuntu AWS EC2 (Free Tier)
Step 2 — Security Group & key pair
Step 3 — SSH into the server
Step 4 — Install Node.js, npm, git & Nginx
Step 5 — Start Nginx & verify
Step 6 — Clone your React repo to the server
Step 7 — Quick UI tweak to prove workflow
Step 8 — npm install & npm run build
Step 9 — Deploy build/ to /var/www/html
Step 10 — Edit Nginx config & validation (Important!)
Step 11 — Troubleshooting checklist
Full command block — Copy-Paste
Next steps — make it production-ready
Short version — Summary / Overview
Goal: Host a React SPA (Single Page Application) on an Ubuntu EC2 instance with Nginx serving the production
build/
so the app is publicly reachable.-
Why:
- React outputs static assets;
- Nginx serves them fast;
- EC2 gives you a public endpoint.
-
Expanded architecture:
Developer laptop → GitHub / local build artifacts
SSH (key) → EC2 Ubuntu server (t2.micro/t3.micro)
On EC2:
git clone
→npm install
→npm run build
→build/
Deploy: copy
build/*
→/var/www/html
→ Nginx serves files and rewrites routes toindex.html
Public: Browser →
http://<EC2-PUBLIC-IP>
→ Nginx → React app
Why this guide exists — a quick story
I once refreshed a deployed site and saw the default Nginx page — pure panic. This guide is the “how I did it” playbook to avoid that 3 AM freakout. Real commands, screenshots, & a repeatable flow.
Prerequisites
Active AWS account (Free Tier OK)
React app (local or GitHub)
EC2 key pair (
.pem
) and SSH knowledgeBasic terminal skills
Step 1 — Launch Ubuntu AWS EC2 (Free Tier)
Choose Ubuntu Server 22.04 LTS AMI.
Pick
t2.micro
/t3.micro
for Free Tier eligibility.Create & download key pair (
demo-react-key.pem
) — guard it like your wallet.
Step 2 — Security Group & key pair
-
Inbound security rule (add these while configuring aws instance):
SSH — TCP 22 — restrict to your-IP (recommended)
-
type = ssh — protocol = tcp — port = 22 — source = custom — <your-ip>/32
→ [single device — Total Number of Hosts: 1] - Check your-ip !
- OR if that causes access issues:
<your-ip>/24
→ [Total Number of Hosts: 256] — preferred-option when behind a small NAT range Copy your public IP from
https://checkip.amazonaws.com/
and use it here.HTTP — TCP 80 — 0.0.0.0/0
type = http — protocol = tcp — port = 80
Tip: keep SSH locked to your IP. Don’t be casual with port 22.
Why this matters: restricting SSH to your IP prevents random scanning and brute-force attempts. Using
/32
is the most secure —/24
is a fallback when you’re on a network where your public IP may appear within a small block.
Step 3 — SSH into the server
On your machine:
chmod 400 demo-react-key.pem
ssh -i demo-react-key.pem ubuntu@<your-ec2-public-ip>
First connect accepts the host key; type
yes
to continue.
Step 4 — Install Node.js, npm, git & Nginx
On the EC2 Instance:
sudo apt update
sudo apt install -y nodejs npm git nginx
node -v && npm -v
For a specific Node version later use
nvm
or NodeSource; apt is fine for demos.
Step 5 — Start Nginx & verify
Start and enable Nginx:
sudo systemctl start nginx
sudo systemctl enable nginx
systemctl status nginx
Visit
http://<your-ec2-public-ip>
— Nginx will now greet you with their basic welcome page.
Step 6 — Clone your React repo to the server
Bring the code:
git clone https://github.com/<your-username>/<your-repo>.git
cd <your-repo>
Cloning is repeatable and easier to track for deploys.
Step 7 — Quick UI tweak to prove workflow
Make a visible change:
cd src
nano App.js # make a small text change & save
cd ..
Step 8 — npm install & npm run build
Build production assets:
npm install
npm run build
Result:
build/
directory containing static files.
Step 9 — Deploy build/
to /var/www/html
Important note: the
build
directory is created inside your project root i.e.my-react-app/
— & not insidemy-react-app/src/
. That means before copying you must ensure you are in the project root (one directory up fromsrc
) so the pathbuild/*
exists.Correct flow (from inside your project root
my-react-app/
):
# make sure you are at: ~/my-react-app
pwd # confirm you are in the project root, not src/ (should show .../my-react-app)
sudo rm -rf /var/www/html/*
sudo cp -r build/* /var/www/html/
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html
If you accidentally run the
cp
from insidesrc/
, thebuild/
path will not exist — move one directory up:cd ..
before running thecp
command.
Quick sanity check:
ls build
should listindex.html
and static folders before you copy.
Step 10 — Edit Nginx config & validation (Important!)
Important: The active Nginx site config for the default site is located at
/etc/nginx/sites-available/default
. You must edit this file to add SPA-friendly routing and other settings — do NOT rely on rewriting the config via a single echo pipe in an unattended script without first verifying the edit.Steps:
Open the config file for editing:
sudo vi /etc/nginx/sites-available/default
Add/ensure the
location/block
includes the SPA rewrite:
server {
listen 80;
server_name _;
root /var/www/html;
index index.html;
location / {
try_files $uri /index.html;
}
error_page 404 /index.html;
}
Save and exit the editor
[Esc → :wq]
.
Now to test the edited config — Run:
sudo nginx -t
If it returns
ok
, the config is syntactically valid. If not, review the line numbers and fix syntax errors (missing semicolons, braces, etc).
Only after
nginx -t
reports OK, restart Nginx to apply changes:
sudo systemctl restart nginx
-
Verify:
sudo systemctl status nginx
and test your site in the browser.Why this change? Editing the config file manually first lets you confirm the file was properly updated (and commented/annotated) before restarting Nginx. That prevents downtime from a broken automated replacement and gives you room to validate.
Step 11 — Troubleshooting checklist
No site → Check Security Group: port 80 must be open.
Nginx down →
sudo systemctl status nginx
.Config syntax errors →
sudo nginx -t
will show line/column issues.Blank / broken app → Check browser console errors and
ls /var/www/html
.Logs:
sudo tail -n 100 /var/log/nginx/error.log
Full command block — Copy-Paste
# 1) Local: set permissions on your key and SSH into EC2
chmod 400 demo-react-key.pem
ssh -i demo-react-key.pem ubuntu@<your-ec2-public-ip>
# 2) On the EC2: update & install required packages
sudo apt update
sudo apt install -y nodejs npm git nginx
# 3) Start & enable nginx service
sudo systemctl start nginx
sudo systemctl enable nginx
# 4) Clone your repo into the home directory (change to your repo)
git clone https://github.com/<your-username>/<your-repo>.git
cd <your-repo>
# 5) Build the React app (ensure you are in the project root)
npm install
npm run build
# 6) Deploy build into Nginx root (confirm you are in project root, not src/)
pwd # confirm path ends with your project root
sudo rm -rf /var/www/html/*
sudo cp -r build/* /var/www/html/
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html
# 7) Edit Nginx config manually:
# sudo nano /etc/nginx/sites-available/default
# Add the location / block with "try_files $uri /index.html;"
# Save file. Then validate & restart (run these two commands manually)
# sudo nginx -t
# sudo systemctl restart nginx
Next steps — make it production-ready (smart moves)
Reserve an Elastic IP so the instance IP doesn’t change.
Add HTTPS with Certbot (Let’s Encrypt).
Automate build & deploy with GitHub Actions or keep
deploy.sh
for local CI.Containerize with Docker when you need portability or scaling.
P.S. This post is part of the FREE DevOps Cohort run by Pravin Mishra. You can start your DevOps journey for free from his YouTube Playlist
This content originally appeared on DEV Community and was authored by Suvrajeet Banerjee