DEV Community

Cover image for Deploy a React App on Ubuntu Web Server in AWS CLoud [week-1]
Suvrajeet Banerjee
Suvrajeet Banerjee Subscriber

Posted on

Deploy a React App on Ubuntu Web Server in AWS CLoud [week-1]

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.


terminal + server rack + Nginx logo


๐Ÿ“š 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 to index.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 knowledge
  • ๐Ÿงฐ Basic 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.

EC2 launch & AMI selection


๐Ÿ”’ 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.

Security Group inbound rules

ip-subnet-calculator

check-ip.aws

๐Ÿ’ก 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>
Enter fullscreen mode Exit fullscreen mode
  • โœ… First connect accepts the host key; type yes to continue.

SSH connection


โš™๏ธ 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
Enter fullscreen mode Exit fullscreen mode
  • โœ… For a specific Node version later use nvm or NodeSource; apt is fine for demos.

packages installation


๐Ÿ•ธ๏ธ Step 5 โ€” Start Nginx & verify

  • ๐Ÿ Start and enable Nginx:
sudo systemctl start nginx
sudo systemctl enable nginx
systemctl status nginx
Enter fullscreen mode Exit fullscreen mode
  • ๐ŸŒ Visit http://<your-ec2-public-ip> โ€” Nginx will now greet you with their basic welcome page.

nginx


๐Ÿ“ฅ 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>
Enter fullscreen mode Exit fullscreen mode
  • ๐Ÿ” Cloning is repeatable and easier to track for deploys.

git clone


โœ๏ธ Step 7 โ€” Quick UI tweak to prove workflow

  • ๐Ÿ–Š๏ธ Make a visible change:
cd src
nano App.js   # make a small text change & save
cd ..
Enter fullscreen mode Exit fullscreen mode

App.js


๐Ÿ—๏ธ Step 8 โ€” npm install & npm run build

  • ๐Ÿ—๏ธ Build production assets:
npm install
npm run build
Enter fullscreen mode Exit fullscreen mode
  • ๐Ÿ“ Result: build/ directory containing static files.

npm run build

npm run build-original


๐Ÿ“ฆ 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 inside my-react-app/src/. That means before copying you must ensure you are in the project root (one directory up from src) so the path build/* 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
Enter fullscreen mode Exit fullscreen mode
  • ๐Ÿ” If you accidentally run the cp from inside src/, the build/ path will not exist โ€” move one directory up: cd .. before running the cp command.

copy build

๐Ÿ’ก Quick sanity check: ls build should list index.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:

  1. ๐Ÿ” Open the config file for editing:
  sudo vi /etc/nginx/sites-available/default
Enter fullscreen mode Exit fullscreen mode
  • โž• Ensure the location / block includes the SPA:
  server {
      listen 80;
      server_name _;
      root /var/www/html;
      index index.html;

      location / {
          try_files $uri /index.html;
      }

      error_page 404 /index.html;
  }
Enter fullscreen mode Exit fullscreen mode
  • ๐Ÿ’ฌ Save and exit the editor [Esc โ†’ :wq].
  1. โœ… Now to test the edited config โ€” Run:
  sudo nginx -t
Enter fullscreen mode Exit fullscreen mode

nginx-ok

  • ๐Ÿ“ If it returns ok, the config is syntactically valid. If not, review the line numbers and fix syntax errors (missing semicolons, braces, etc).
  1. ๐Ÿ” Only after nginx -t reports OK, restart Nginx to apply changes:
  sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode
  • ๐Ÿ”Ž 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
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ฎ 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


Top comments (0)