๐ŸŽ‰ Limited-time Hostinger deal โ€” up to 75% off + extra with codeWEBHOST2026Get my link โ†’
H
Tutorials

Host a MERN stack app cheaply in 2026

11 min read

Three realistic ways to put MongoDB, Express, React and Node online for roughly the price of a coffee โ€” with the security steps most tutorials skip.

The three pieces, and where each one can live

A MERN app is really three deployables: a React bundle (static files), an Express/Node API (a long-lived process) and MongoDB (a database that needs persistent storage). Cost decisions get much easier once you stop thinking of it as one thing.

The static React build is nearly free to host anywhere. The API needs somewhere that will keep a process alive. The database is where the real money and the real risk live โ€” and it is the piece most people get wrong.

Three setups and what they actually cost

Prices below were checked in September 2026 and will drift. The trade-off column is the part that stays true.

SetupRoughly per monthThe trade-off
One VPS + MongoDB Atlas M0VPS only, DB freeAtlas M0 is genuinely free forever, but caps at 512 MB storage and ~100 ops/sec. Fine for a portfolio or an MVP; it will not carry a real product.
One VPS running everything, Mongo in DockerVPS onlyNo database ceiling and no second bill, but backups, upgrades and security are entirely yours. This is the cheapest path with room to grow.
Managed PaaS + Atlas Flex~$7โ€“37 for the app, up to $30 for the DBLeast operational work, highest and least predictable bill. Usage-based pricing can surprise you.

The cheapest sane setup: everything on one box

A single entry-tier VPS with 4 GB of RAM runs an Express API, a React build and MongoDB simultaneously without breaking a sweat, and the cost does not change when you add your next side project. Here is the whole thing.

Start by installing Node 24 LTS โ€” the Active LTS line as of September 2026 โ€” and Docker, which we will use for MongoDB so you are not chasing apt repository changes every time Mongo ships a major version.

On a fresh Ubuntu 24.04 or 26.04 server, as a sudo user:
bash
# Node 24 LTS
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt-get install -y nodejs nginx git

# Docker, for MongoDB
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
# log out and back in so the group takes effect

Run MongoDB without putting it on the public internet

This is the step to get right. Exposed, unauthenticated MongoDB instances have been the source of a long run of data breaches, and the default Docker command in most tutorials publishes the port to the world.

Two things make it safe: bind the published port to `127.0.0.1` so only processes on the same machine can reach it, and set root credentials on first run. Note that Docker writes its own iptables rules and can punch straight through UFW โ€” a plain `-p 27017:27017` really is reachable from the internet even with a firewall you believe is closed.

The leading 127.0.0.1 in the port mapping is the entire security story:
bash
docker volume create mongo-data

docker run -d \
  --name mongo \
  --restart unless-stopped \
  -p 127.0.0.1:27017:27017 \
  -v mongo-data:/data/db \
  -e MONGO_INITDB_ROOT_USERNAME=appuser \
  -e MONGO_INITDB_ROOT_PASSWORD='use-a-long-random-password' \
  mongo:8
Verify it is NOT listening on a public interface:
bash
sudo ss -tlnp | grep 27017
# Want:  127.0.0.1:27017
# Bad:   0.0.0.0:27017  <- reachable from the internet

Run the Express API under PM2

PM2 restarts the API if it crashes and brings it back after a reboot. Bind it to loopback as well โ€” Nginx is the only thing that should be able to reach it.

bash
sudo npm install -g pm2

cd /var/www/api
npm ci --omit=dev

pm2 start server.js --name api
pm2 save
pm2 startup systemd
# run the sudo command PM2 prints, then 'pm2 save' again
In server.js, listen on loopback only:
js
const PORT = process.env.PORT || 5000;

app.listen(PORT, "127.0.0.1", () => {
  console.log("API listening on 127.0.0.1:" + PORT);
});

Serve the React build and proxy the API from one domain

Serving the front end and API from the same origin removes your CORS problems entirely โ€” the browser sees one domain. Nginx serves the static build from disk and forwards anything under `/api/` to Express.

The `try_files` line is what makes client-side routing work. Without it, a hard refresh on `/dashboard` returns a 404 because no such file exists on disk.

Build the React app and put it where Nginx can find it:
bash
cd /var/www/client
npm ci
npm run build
# Vite outputs to dist/, Create React App to build/
/etc/nginx/sites-available/mern
nginx
server {
    listen 80;
    server_name example.com;

    root /var/www/client/dist;
    index 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;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Client-side routing: unknown paths fall back to index.html
    location / {
        try_files $uri $uri/ /index.html;
    }
}
Enable it, then add free HTTPS:
bash
sudo ln -s /etc/nginx/sites-available/mern /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com

Back up the database, because nobody else will

This is the cost of self-hosting Mongo, and it is small. A nightly `mongodump` into a dated folder, with anything older than two weeks deleted, covers the realistic failure modes: a bad migration, a dropped collection, a corrupted write.

Weekly server-level backups from your host protect you from losing the whole machine. They do not protect you from deleting a collection on Tuesday afternoon. You want both.

/home/deploy/backup-mongo.sh
bash
#!/usr/bin/env bash
set -euo pipefail

STAMP=$(date +%F)
OUT=/home/deploy/backups/$STAMP
mkdir -p "$OUT"

docker exec mongo mongodump \
  --username appuser \
  --password "$MONGO_PASSWORD" \
  --authenticationDatabase admin \
  --archive > "$OUT/myapp.archive"

# Keep two weeks
find /home/deploy/backups -maxdepth 1 -type d -mtime +14 -exec rm -rf {} +
Schedule it with crontab -e:
bash
0 3 * * * MONGO_PASSWORD='...' /home/deploy/backup-mongo.sh >> /home/deploy/backup.log 2>&1

When to stop self-hosting the database

Running Mongo yourself is the right call while your data fits comfortably on one disk and downtime during a restore is survivable. Move to managed hosting when any of the following become true โ€” and notice that all three are about consequences, not scale.

  • A few hours of downtime during a restore would genuinely hurt the business.
  • You need point-in-time recovery rather than 'yesterday at 3am'.
  • You want replica-set failover, and standing one up yourself is no longer a fun afternoon.

The short version

For a side project or an MVP, one VPS running React, Express and Mongo together is the cheapest arrangement that does not box you in, and the price stays flat as you add projects. Start on Atlas M0 if you would rather not own a database yet; move Mongo onto the box when 512 MB stops being enough.

Coupon WEBHOST2026 applies automatically through the links here if you are picking up a server for this.

Ready to save with coupon WEBHOST2026?

Get my discount link โ†’

Ready to claim your Hostinger discount?

Generate your personalised cart link with coupon WEBHOST2026 attached โ€” it takes 20 seconds.