© Ujjawal Solanki | All Rights Reserved
The first time my MERN app worked end to end on localhost, I felt unstoppable. Then someone asked for the link, and I realized I had built a private miracle. It lived only on my laptop.
That moment hurt in a useful way. Until then, deployment felt like a future problem. I was obsessed with routes, forms, and Mongo queries. I had not built the skill of making software reachable.
So I rented a small VPS, opened an SSH session, and immediately entered the most humbling phase of my early developer life. Every command felt serious. Every mistake felt expensive.
Most deployment tutorials are only scary because they skip the mental model. They throw ten commands at you and expect confidence to appear on its own.
What finally helped me was seeing the server as a normal computer with a few jobs. One process serves the API. One reverse proxy accepts public traffic. One build folder serves React. One database connection points to MongoDB. That is the story.
If you feel overwhelmed on your first VPS, that is not proof you are bad at deployment. It is proof that deployment is a separate skill from coding.
That checklist sounds basic, but it saved me from the most embarrassing class of deploy bugs: the ones caused by panic and memory.
I start by updating packages and installing only the essentials. When I used to install random extras because a tutorial mentioned them, I created more confusion than value.
You want a clean base: Node, npm, Git, Nginx, and a process manager. That is enough to get a real MERN app online.
ssh root@your_server_ip
sudo apt update && sudo apt upgrade -y
sudo apt install -y nodejs npm git nginx
node -v
npm -vBefore I even think about Nginx, I make sure the API can boot with its own environment file. This is where hidden assumptions show up fast.
If the app crashes here, that is good news. It means the problem is still small. Fix missing environment variables, bad ports, or hardcoded localhost references now.
git clone https://github.com/your-username/your-mern-app.git
cd your-mern-app/server
npm install
printf 'PORT=5000
MONGO_URI=your_atlas_uri
JWT_SECRET=super_secret
' > .env
node index.jsMy early mistake was believing that if a Node process was running in my SSH tab, the app was deployed. Then I closed the terminal and the app disappeared.
PM2 solves that beginner pain immediately. It also gives you logs, restart control, and a calmer recovery story when something fails.
sudo npm install -g pm2
cd /root/your-mern-app/server
pm2 start index.js --name mern-api
pm2 save
pm2 status
pm2 logs mern-apiThis was the turning point for me. Once I understood that React becomes static files after build, Nginx stopped feeling mysterious.
Serve the frontend from Nginx and proxy `/api` to the Node process. That way the browser talks to one domain, and your server routes traffic intelligently.
cd /root/your-mern-app/client
npm install
npm run build
sudo rm -rf /var/www/mern-app
sudo mkdir -p /var/www/mern-app
sudo cp -r dist/* /var/www/mern-app/server {
listen 80;
server_name yourdomain.com;
root /var/www/mern-app;
index index.html;
location / {
try_files $uri /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:5000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}I used to rush to SSL because it felt like the final badge of a real deploy. But if the site does not serve correctly on plain HTTP first, SSL only hides the actual problem.
Once the domain and Nginx config are confirmed, Certbot is usually the easy part.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.comHalf my early deploy failures came from missing or mismatched environment variables. The app did not fail loudly enough. PM2 kept restarting it, and I kept pretending the issue was Nginx.
Sometimes React created `build/`. Sometimes Vite created `dist/`. I copied the wrong folder more than once and stared at a broken landing page like it had betrayed me.
If your frontend still calls `http://localhost:5000`, the app may look fine in development and fail beautifully in production. Always review API base URLs before deploying.
When deployment feels confusing, read logs before changing architecture. `pm2 logs` and `sudo nginx -t` tell the truth faster than guessing.
What made deployment finally click for me was realizing I did not need to be clever. I needed to be methodical. The calmer I became, the faster I fixed things.
The first live link I shipped did not look glamorous. But it taught me something bigger than commands. Software is only real when people can reach it.
If this helped you, save it for later. And if you are stuck on a MERN feature, you can always reach out to me.
© Ujjawal Solanki | All Rights Reserved