AWS Builder Center

Deploying a Full-Stack MERN Application on AWS: A Beginner-Friendly Guide

Learn how to deploy a MERN stack application on AWS using services such as EC2, MongoDB Atlas, and S3. This guide explains the deployment flow, environment variables, security considerations, and common challenges developers face when moving a project from localhost to the cloud.

Deploying a Full-Stack MERN Application on AWS: A Beginner-Friendly Guide

Building a MERN application locally is one thing, but deploying it so that users can access it from anywhere is where cloud platforms like AWS become extremely useful.
As a developer working with React, Node.js, Express.js, and MongoDB, I wanted to understand how a project moves from localhost to a real production environment.
In this article, I’ll explain a simple deployment architecture for a MERN application using AWS.

1. Understanding the Architecture

A typical MERN application consists of:
  • React.js → Frontend
  • Node.js + Express.js → Backend/API
  • MongoDB → Database
A simple AWS-based architecture can look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Users
|
↓
Internet
|
↓
React Frontend
|
↓
Node.js / Express
Backend
|
↓
MongoDB Atlas
For the AWS infrastructure, we can use services such as:
  • Amazon EC2 – Host the Node.js backend
  • Amazon S3 – Host static frontend files
  • Amazon CloudFront – Deliver frontend content globally
  • MongoDB Atlas – Host the database
  • AWS IAM – Manage permissions and access
  • Route 53 – Manage domain names, if required

2. Preparing the Backend

Before deploying, make sure the Express backend works correctly on your local machine.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const express = require("express");

const app = express();

app.use(express.json());

app.get("/", (req, res) => {
res.json({ message: "API is running" });
});

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
One important change is using:
1
process.env.PORT
instead of hardcoding the port.
This allows the application to use environment-specific configurations.

3. Managing Environment Variables

Never put sensitive information directly inside your source code.
For example, avoid:
1
const MONGO_URI = "mongodb+srv://username:password@cluster...";
Instead, use environment variables:
1
2
3
PORT=5000
MONGO_URI=your_mongodb_connection_string
JWT_SECRET=your_secret
And access them using:
1
process.env.MONGO_URI
This becomes especially important when your application contains:
  • Database credentials
  • JWT secrets
  • API keys
  • Third-party service credentials

4. Deploying the Backend on EC2

Amazon EC2 provides virtual servers that can run our backend application.
The basic deployment flow is:
1
2
3
4
5
6
7
8
9
10
11
12
13
Create EC2 Instance
↓
Connect using SSH
↓
Install Node.js
↓
Clone GitHub Repository
↓
Install Dependencies
↓
Configure Environment Variables
↓
Start Backend
After connecting to the instance, we can install the required dependencies and clone our project.
For example:
1
2
3
git clone <your-repository>
cd backend
npm install
Then configure the environment variables and start the application.
For a production application, using a process manager such as PM2 can help keep the Node.js process running.

5. Deploying the React Frontend

The React application needs to be built before deployment.
For a Vite application:
1
npm run build
This generates the production build.
The generated files can be hosted using Amazon S3 for static website hosting, with CloudFront used as a CDN for better content delivery.
The flow becomes:
1
2
3
4
5
6
7
8
9
10
11
React Source Code
↓
npm run build
↓
Production Build
↓
S3
↓
CloudFront
↓
Users
This separates the frontend from the backend and allows each part to be managed independently.

6. Connecting Frontend and Backend

During local development, the frontend might communicate with:
1
http://localhost:5000
After deployment, this needs to be replaced with the production API URL.
For example:
1
VITE_API_URL=https://api.example.com
Then the frontend can make requests using:
1
fetch(`${import.meta.env.VITE_API_URL}/api/users`);
This is one of the most common changes required when moving an application from development to production.

7. Don't Forget CORS

If the frontend and backend are hosted on different domains, CORS needs to be configured properly.
For example:
1
2
3
4
5
const cors = require("cors");

app.use(cors({
origin: "https://example.com"
}));
During development, developers often use:
1
app.use(cors());
But in production, allowing every origin may not be appropriate.
It's better to explicitly allow the frontend's domain.

8. Security Considerations

Deployment is not just about making the application accessible.
Security should also be considered.
Some important practices include:

Use IAM properly

Avoid using highly privileged AWS credentials unnecessarily.
Give users and applications only the permissions they actually need.

Protect environment variables

Never commit .env files containing secrets to GitHub.
Add:
1
.env
to .gitignore.

Configure EC2 Security Groups

Only expose the ports that are actually required.
For example:
1
2
3
22 → SSH
80 → HTTP
443 → HTTPS
Avoid opening unnecessary ports to the public internet.

Use HTTPS

For a production application, HTTPS should be used to encrypt communication between users and your application.

9. What I Learned

The biggest lesson for me was that deployment is not simply uploading code to a server.
There are several components involved:
1
2
3
4
5
6
7
8
9
10
11
Application
+
Infrastructure
+
Networking
+
Security
+
Environment Configuration
+
Monitoring
Understanding how these pieces work together makes it much easier to troubleshoot production issues.
For example, if an API works locally but not after deployment, the problem could be related to:
  • Security Groups
  • Environment variables
  • CORS
  • DNS
  • Backend process
  • Database network access
  • HTTPS configuration

10. Final Thoughts

AWS provides a large ecosystem of services that can initially feel overwhelming.
Instead of trying to learn every AWS service at once, I believe developers can start with a small application and gradually understand the services required to deploy and operate it.
For a MERN developer, a good starting path is:
1
2
3
4
5
6
7
8
9
10
11
EC2
↓
IAM
↓
S3
↓
CloudFront
↓
Route 53
↓
CloudWatch
Once the fundamentals are clear, it becomes much easier to explore more advanced AWS concepts such as containers, serverless architectures, load balancing, autoscaling, and CI/CD.
My goal is to continue learning AWS by deploying real projects rather than only studying the services theoretically.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article