Install the MEAN stack as four cooperating services: MongoDB stores data, Express handles HTTP routes, Angular supplies the browser interface, and Node.js runs the server-side JavaScript. On a production VPS, keep Express and MongoDB on private interfaces, serve Angular through NGINX on ports 80 and 443, and run the API with PM2 so it survives SSH disconnects and reboots.
Antihost offers Linux VPS plans with full root access, unlimited traffic on a 1 Gbps port and a 14-day free trial.
What you are deploying
MEAN is an acronym for MongoDB, Express.js, Angular and Node.js. MongoDB is the database layer; Express is the web application and routing layer; Node.js is the JavaScript runtime that executes Express; and Angular is the presentation layer compiled into browser-ready static files.
As an Amazon Associate I earn from qualifying purchases.
A practical VPS layout looks like this:
- Public traffic: NGINX listens on ports 80 and 443.
- Frontend: NGINX serves Angular’s production build and falls back to
index.htmlfor client-side routes. - API: NGINX proxies an
/apiprefix to Express on an internal port such as 5050. - Database: MongoDB is bound locally, or the application connects to a restricted MongoDB Atlas cluster.
- Process supervision: PM2 starts and monitors the Express process.
The public client never needs direct access to MongoDB. Keeping the database and Express listener off the public interface reduces the number of services exposed to the internet.
Choose compatible versions before installing
Do not treat “latest” as a compatibility strategy. Choose a supported Ubuntu LTS image, then select Node.js, Angular CLI and MongoDB releases that the application and each other support. Older MEAN.JS documentation specifically warns that unsupported preinstalled versions can break a project. Record the choices in your deployment notes and commit lockfiles with the application.
#1 Best Overall
| Component | What to pin or verify | Why it matters |
|---|---|---|
| Ubuntu | Supported LTS release matching the MongoDB repository instructions | Repository packages and security updates are release-specific. |
| Node.js | One LTS line compatible with the Angular and Express dependencies | Native modules and framework tooling can fail after a runtime change. |
| npm | The version installed with the selected Node.js release | Lockfile format and dependency resolution can differ between npm versions. |
| Angular CLI | The major version expected by the project | The CLI, Angular packages and Node.js runtime must be compatible. |
| MongoDB | A server release supported on the selected Ubuntu LTS | Repository names, service units and authentication defaults vary by release. |
One-click images can change these defaults without notice. Inspect the image manifest and application requirements before using one for production.
Prepare the Linux VPS
1. Create a non-root deployment account
Log in initially with the provider’s administrative account, create a dedicated user, and grant only the administrative access needed for maintenance:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
sudo adduser deploy
sudo usermod -aG sudo deploy
Install your public SSH key for the deploy account, test a second SSH session, and only then consider disabling password authentication or direct root login in the SSH configuration. Keep an existing administrative session open while testing so a syntax mistake cannot lock you out.
2. Update the operating system
sudo apt update
sudo apt upgrade -y
Reboot if the kernel or a core service was upgraded, then reconnect as deploy. Confirm the hostname and system clock before creating TLS certificates or debugging logs.
3. Allow only required network ports
For a normal web deployment, the firewall needs SSH, HTTP and HTTPS. With UFW, use:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
Do not open MongoDB’s 27017 port to the internet merely to make the application work. If a managed database requires an allow-list, permit the VPS’s outbound address in that service instead.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesInstall Node.js and npm
Use the installation method recommended for the exact Ubuntu release you selected, whether that is the distribution package, a provider image, or a version manager approved by your operations process. Avoid mixing packages from unrelated Ubuntu releases. After installation, verify and record the runtime:
node --version
npm --version
MongoDB’s current framework quick start asks for the Latest LTS or Latest Release of Node.js, but a production application should follow its tested compatibility range rather than changing the runtime automatically. Re-run these checks after every image rebuild or major upgrade.
Install MongoDB or use MongoDB Atlas
Self-host MongoDB on the VPS
Follow MongoDB’s repository instructions for the precise Ubuntu release. The repository key, package name and service unit are release-dependent; do not paste instructions for a different distribution. Once installed, enable the service and check its status. Depending on the package, the service may be named mongod or mongodb:
sudo systemctl enable --now mongod
sudo systemctl status mongod
If your package uses the alternate unit name, substitute it consistently. Keep MongoDB bound to localhost unless you have a documented private-network design. Create a dedicated application database user rather than connecting from Express as an administrator. A typical mongosh session is:
mongosh
use admin
db.createUser({
user: "meanapp",
pwd: passwordPrompt(),
roles: [{ role: "readWrite", db: "meanapp" }]
})
Enable authorization and restart MongoDB according to the version’s official instructions. Store the resulting connection string as a secret, for example mongodb://meanapp:[email protected]:27017/meanapp?authSource=admin. In real deployments, use a secret value containing URL-escaped credentials instead of putting a literal password in a shell history.
Use MongoDB Atlas instead
Atlas is a fully managed MongoDB service, so the VPS does not run the database daemon. Create a cluster, create a least-privilege database user, restrict network access to the VPS’s address or private network, and copy the generated connection URI. Save it securely as MONGODB_URI; never commit it to Git or paste it into client-side Angular code.
Whichever option you choose, verify database authentication from the VPS before wiring the application. A successful local connection does not prove that the production user, network allow-list or TLS settings are correct.
Create the Express and Node.js backend
1. Create the project directories
sudo mkdir -p /var/www/mean
sudo chown -R deploy:deploy /var/www/mean
cd /var/www/mean
mkdir server
cd server
npm init -y
Keep the backend source and Angular build in separate directories. This makes it possible to replace the frontend without overwriting the API’s package files.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware match2. Install the server dependencies
MongoDB’s official example uses Express, the MongoDB driver, CORS and dotenv at runtime, with TypeScript tooling available for a typed project:
npm install mongodb express cors dotenv
npm install -D typescript @types/node @types/cors @types/express tsx
If your application is plain JavaScript, the development TypeScript packages are optional; retain only the packages your source actually imports. Commit the generated package-lock.json.
3. Keep configuration outside source code
Create a server-side .env file that is readable only by the deployment account and contains the database URI and internal port:
MONGODB_URI=mongodb://meanapp:[email protected]:27017/meanapp?authSource=admin
PORT=5050
Add .env to .gitignore. MongoDB’s quick start uses the MONGODB_URI environment variable and instructs users to save the connection string securely. Do not expose this file through NGINX or copy it into the Angular dist directory.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →4. Add a small health-checking server
The following is a minimal JavaScript starting point. Replace the example route with your application’s controllers and validation:
require("dotenv").config();
const express = require("express");
const cors = require("cors");
const { MongoClient } = require("mongodb");
const app = express();
const port = process.env.PORT || 5050;
const client = new MongoClient(process.env.MONGODB_URI);
app.use(cors());
app.use(express.json());
app.get("/api/health", (_req, res) => {
res.json({ ok: true });
});
async function start() {
await client.connect();
app.locals.db = client.db();
app.listen(port, "127.0.0.1", () => {
console.log(`Express listening on ${port}`);
});
}
start().catch((error) => {
console.error(error);
process.exit(1);
});
The example listens on 5050, matching MongoDB’s current framework example. Some provider images use 5000 instead. Choose one port, use it consistently in Express, NGINX and your process manager, and do not expose it in the firewall.
MongoDB’s sample application mounts routes under /restaurant. If you retain that pattern, make the NGINX prefix and Angular service URLs match it; do not assume sample data or route names belong in your application.
Create and build the Angular frontend
1. Install the CLI and create or clone the project
sudo npm install -g @angular/cli
cd /var/www/mean
ng new client
cd client
npm install
If you already have an Angular repository, clone it into /var/www/mean/client instead of running ng new. Keep the CLI major version aligned with the project’s Angular packages.
2. Point the application at the reverse proxy
Configure the production environment or API service to use a relative URL such as /api, not http://127.0.0.1:5050. A browser interprets localhost as the visitor’s computer, not the VPS. Using the same origin also avoids unnecessary cross-origin requests in production.
During local development you can use Angular’s development proxy, but production requests should follow the NGINX path. If the backend’s routes are mounted below another prefix, use that exact prefix in the Angular service.
Rank #4
3. Build for production
ng build --configuration production
Angular writes the compiled files below dist. The exact nested directory can vary by project and Angular version, so inspect the output and record the path, for example /var/www/mean/client/dist/client/browser. NGINX must point to the directory containing the generated index.html, not merely to the parent dist directory.
Recommended Free Tools
Put NGINX in front of Angular and Express
1. Create a server block
Install NGINX using the Ubuntu package for your release, then create a site configuration such as /etc/nginx/sites-available/mean. Replace the domain and Angular output path:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/mean/client/dist/client/browser;
index index.html;
location /api/ {
proxy_pass http://127.0.0.1:5050;
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;
}
location / {
try_files $uri $uri/ /index.html;
}
}
The try_files fallback is essential for Angular’s client-side routing: a direct request for /games/42 must return the Angular shell rather than a 404 from NGINX. The proxy sends only API requests to Express while static assets remain publicly cacheable files.
2. Enable and validate the site
sudo ln -s /etc/nginx/sites-available/mean /etc/nginx/sites-enabled/mean
sudo nginx -t
sudo systemctl reload nginx
Remove or disable the default site if it claims the same server name. Fix every error reported by nginx -t before reloading. Check file permissions if NGINX returns 403: the NGINX worker must be able to traverse each parent directory and read the Angular build.
3. Add HTTPS after DNS points to the VPS
Create DNS records for the chosen hostname, confirm that ports 80 and 443 reach the VPS, and use the supported Certbot workflow for your Ubuntu and NGINX installation. A typical package-based workflow is:
Free tools Windows power users keep installed
One-click scans. No signup required.
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
Review the generated server block, test renewal with the command recommended by your Certbot package, and keep port 80 available if your renewal method needs HTTP validation. Do not place database credentials in NGINX configuration.
Keep Express running with PM2
1. Start the API
Install PM2 globally and launch the backend from its directory:
sudo npm install pm2@latest -g
cd /var/www/mean/server
pm2 start server.js --name mean-api
If your entry point has a different name, substitute it. For a TypeScript application, use the project’s documented tsx or compiled JavaScript start command rather than asking PM2 to execute TypeScript blindly.
Best Value
2. Save and enable startup persistence
pm2 save
pm2 startup
pm2 startup prints a command containing the user and a required privilege escalation. Copy and run that printed command exactly, then save the process list again:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →pm2 save
pm2 ls
pm2 logs mean-api
The first command records the current process list; the generated startup unit restores it after a VPS reboot. Without both steps, the API can disappear when SSH closes or the machine restarts.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Verify the deployment in layers
- Database: From the VPS, confirm that the application credentials authenticate and that the expected database is selected.
- Express: Test the internal listener before involving NGINX:
curl http://127.0.0.1:5050/api/health. Expect the JSON health response from the sample route. - NGINX: Request the domain over HTTP and confirm that the Angular
index.htmlis returned. Test a deep Angular route, not only the home page. - Proxying: Call the public
/api/healthpath and confirm that it reaches Express without a browser CORS error. - HTTPS: Test both the certificate and HTTP-to-HTTPS behavior from an external network.
- Persistence: Run
pm2 restart mean-api, inspectpm2 logs, and reboot the VPS during a maintenance window. Verify that PM2 restores the process and NGINX still serves the site. - DNS and firewall: Check the domain from a network outside the VPS and verify that only SSH, HTTP and HTTPS are reachable publicly.
These checks distinguish an application failure from a proxy, DNS, firewall or process-supervision failure. Test each layer separately instead of changing several configurations at once.
Manual installation versus a one-click MEAN image
| Consideration | Manual installation | One-click image |
|---|---|---|
| Control | You select the Ubuntu release, package versions, ports and directory layout. | The provider selects image defaults, bundled components and paths. |
| Time to first page | Longer because MongoDB, Node.js, Angular, NGINX and PM2 are configured individually. | Usually faster because the base components are preinstalled or scripted. |
| Maintenance | You own updates, compatibility checks, hardening and backups. | Bootstrap is simpler, but you still own application updates, secrets, TLS and database operations. |
| Port assumptions | You define the internal Express port and NGINX routing. | Provider examples commonly use Express on 5000 and public NGINX ports 80 and 443. |
| Version example | Choose and record the versions that your project supports. | A provider listing crawled in 2026 records Node.js 22.22.3; that value is an image snapshot, not a permanent requirement. |
| Best fit | Teams needing repeatable infrastructure and a known file layout. | Short-lived prototypes or teams willing to inspect and accept provider defaults. |
Before production use, inspect a one-click image’s startup scripts, service names, firewall rules, MongoDB bind address, preinstalled credentials and NGINX root. Rebuild the server from documented steps if you cannot explain what the image changed.
Troubleshoot the failures that occur most often
NGINX returns 502 Bad Gateway
- Check
pm2 lsandpm2 logs mean-api. - Run
curl http://127.0.0.1:5050/api/healthon the VPS. - Confirm that Express’s
PORT, NGINX’sproxy_passport and the PM2 working directory agree. - Inspect the NGINX error log for permission or connection-refused messages.
Angular routes work from links but fail after refresh
Ensure the frontend location contains try_files $uri $uri/ /index.html; and that root points to the directory containing the built shell. Re-run nginx -t and reload after editing.
The browser reports a CORS error
Use a relative production API URL such as /api and proxy that prefix through the same NGINX host. If a separate origin is intentional, configure Express CORS with an explicit allow-list rather than allowing every origin, and ensure preflight requests reach the API.
Express exits immediately
Read the PM2 log for a missing MONGODB_URI, authentication failure, syntax error or incompatible native dependency. Confirm that PM2 starts from /var/www/mean/server and that the account running PM2 can read the environment file. Restart only after correcting the underlying error.
MongoDB connections fail after a reboot
Check the database service with systemctl status mongod (or the service name supplied by your package), verify that authorization settings and the connection URI still match, and confirm that MongoDB is listening on the intended local or private address. Never solve this by opening 27017 globally.
The deployment works until a package update
Compare the recorded Node.js, npm, Angular CLI and MongoDB versions with the versions now installed. Restore the lockfile, use the project’s supported runtime, and test the application in a staging VPS before upgrading production. Keep backups of the database and the previous Angular build so a rollback is possible.
Quick Recap
Production security and maintenance checklist
- Use SSH keys and a non-root deployment account; keep a tested administrative recovery path.
- Allow only ports 22, 80 and 443 unless a documented private-network rule requires another port.
- Keep MongoDB private and create a least-privilege application user.
- Store
MONGODB_URIand other secrets in server-side environment configuration, never in Git or Angular assets. - Terminate TLS at NGINX, renew certificates, and redirect HTTP to HTTPS.
- Pin compatible dependency versions and review lockfile changes before deployment.
- Use PM2 startup persistence and verify recovery after a controlled reboot.
- Back up MongoDB, test restoration, and retain the previous application build for rollback.
- Review NGINX, PM2, Express and MongoDB logs routinely for authentication failures and unexpected traffic.
- Recheck provider image manifests, Ubuntu repository instructions, Node.js releases, Angular compatibility and firewall defaults before rebuilding a VPS; those details change over time.
Deployment checklist
- Supported Ubuntu LTS installed; deploy user and SSH-key access tested.
- System updated; UFW permits SSH, HTTP and HTTPS only.
- Node.js and npm versions recorded and compatible with the project.
- MongoDB installed and secured, or Atlas network access restricted to the VPS.
MONGODB_URIstored outside source code.- Express health route responds on its internal port.
- Angular production build path identified and readable by NGINX.
- NGINX serves the SPA and proxies the chosen API prefix.
- HTTPS certificate installed and renewal checked.
- PM2 process saved, startup command executed, logs clean, and reboot recovery verified.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




