---
title: "How to host a Node.js app on a VPS in Brazil | StreetHosting"
description: "Host a Node.js app or API on a VPS in Brazil: Node 24 LTS, PM2 with automatic restarts, Nginx, custom domain, HTTPS with Certbot and a UFW firewall."
url: "https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps"
type: "page"
language: "en-US"
---

VPS · 9 min · Intermediate

Published on Jun 9, 2026 · Updated on Sep 28, 2026

# Node.js in production on a VPS: PM2, Nginx, domain and HTTPS

A Node.js app in production needs a process manager, a reverse proxy, HTTPS and automatic restarts. Follow the complete walkthrough on an Ubuntu VPS in São Paulo, close to your users and to Brazilian integrations.

By [Equipe StreetHosting](https://streethosting.com.br/en/autores#equipe-streethosting) · StreetHosting infrastructure and support team

[Deploying and running apps](https://streethosting.com.br/en/guides/topics/deploy) [Network, DNS and domains](https://streethosting.com.br/en/guides/topics/networking) [Certificates and HTTPS](https://streethosting.com.br/en/guides/topics/ssl) [Minecraft server administration](https://streethosting.com.br/en/guides/topics/minecraft-administration)

Summarize with:

[](https://chat.openai.com/?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-nodejs-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "ChatGPT") [](https://claude.ai/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-nodejs-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Claude") [](https://www.google.com/search?udm=50&aep=11&q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-nodejs-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Google AI Mode") [](https://x.com/i/grok?text=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-nodejs-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Grok") [](https://www.perplexity.ai/search/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-nodejs-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Perplexity")

Share:

[](https://x.com/intent/tweet?text=How%20to%20host%20a%20Node.js%20app%20on%20a%20VPS%20in%20Brazil&url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-nodejs-on-vps "Share on X") [](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-nodejs-on-vps "Share on Facebook") [](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-nodejs-on-vps "Share on LinkedIn") [](https://wa.me/?text=How%20to%20host%20a%20Node.js%20app%20on%20a%20VPS%20in%20Brazil%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-nodejs-on-vps "Share on WhatsApp")

For agents: Copy as Markdown [.md](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps.md)

In this guide 8 sections

* [Why a VPS in Brazil](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#por-que-vps-local)
* [Recommended stack](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#stack-recomendada)
* [Install Node.js and PM2](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#instalar-node)
* [Step-by-step deploy](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#deploy-passo-a-passo)
* [Automatic restarts with PM2](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#reinicio-automatico)
* [Nginx, domain and HTTPS](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#nginx-https)
* [Common errors](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#erros-comuns)
* [Ryzen or Xeon for Node.js](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#ryzen-ou-xeon)

Quick answer

To **host Node.js on a VPS in Brazil**, use an Ubuntu 24.04 VPS in São Paulo, install Node 24 LTS from NodeSource, run the app with PM2 listening on 127.0.0.1, set up `pm2 startup`so it comes back after a reboot, and put Nginx in front with HTTPS from Let's Encrypt. The firewall allows only SSH, 80 and 443, and light APIs start comfortably with 1 or 2 vCPUs and 2 to 4 GB of RAM.

## Why host Node.js on a VPS in Brazil[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#por-que-vps-local)

Latency matters for mobile apps, PIX integrations and webhooks that talk to Brazilian services. Hosting in São Paulo cuts the round trip by tens of milliseconds compared to US regions, and that adds up on every call: a screen that makes five requests in sequence feels the difference five times. Users perceive the app as more responsive without a single code change.

A VPS also gives you control that managed platforms do not: the exact Node version, background processes, long-lived WebSockets, scheduled tasks and the database on the same private network as the app, with no billing per request or per execution time. In exchange, you look after the system. The rest of this guide shows how to do that with few moving parts and without leaving ports open.

## Recommended stack[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#stack-recomendada)

| Component      | Role                                                          | Where it listens            |
| -------------- | ------------------------------------------------------------- | --------------------------- |
| Node.js 24 LTS | Runs the application                                          | 3000 on 127.0.0.1 only      |
| PM2            | Keeps the process alive, restarts it on failure, stores logs  | No port                     |
| systemd        | Starts PM2 and the apps at boot                               | No port                     |
| Nginx          | Receives traffic, terminates HTTPS and forwards it to the app | 80 and 443, public          |
| Certbot        | Issues and renews the Let's Encrypt certificate               | Uses port 80 for validation |
| UFW            | Blocks everything except SSH, 80 and 443                      | No port                     |

* **Environment variables outside the repository:** passwords and keys live in a `.env` only on the VPS, never in Git.
* **Log rotation:** without it, PM2 logs grow until they fill the disk.
* **App invisible to the internet:** it listens on 127.0.0.1, and only Nginx talks to the outside world.

## Install Node.js and PM2[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#instalar-node)

Before anything else, log in as a regular user with sudo and an SSH key, as shown in the guide on [secure SSH on a VPS](https://streethosting.com.br/en/guides/vps/secure-ssh-linux-vps). Running the app as root means any flaw in it becomes full control of the server.

Install Node from the NodeSource repository, using the 24.x line, which is the active LTS. The package shipped with Ubuntu 24.04 is older, and nvm installs Node inside the user's home folder, which complicates automatic startup at boot. With NodeSource, the binary sits at `/usr/bin/node`, where systemd always finds it.

`curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash - sudo apt install -y nodejs git node -v sudo npm install -g pm2`

Use the same major Node version on the VPS as on your computer and in CI. Version mismatches cause a good share of the errors that only show up in production, such as native dependencies compiled for another version.

## Step-by-step deploy[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#deploy-passo-a-passo)

The code lives in `/var/www`, owned by your user. Clone, install the dependencies and, if the project is in TypeScript, build it:

`sudo mkdir -p /var/www/minha-api sudo chown usuario:usuario /var/www/minha-api git clone https://github.com/sua-conta/minha-api.git /var/www/minha-api cd /var/www/minha-api # Plain JavaScript npm ci --omit=dev # TypeScript: needs the dev dependencies to compile npm ci npm run build npm prune --omit=dev`

`npm ci` installs exactly what is in `package-lock.json`, without updating anything on its own, which is why it is the right command for a server. Then create the environment file, readable only by your user:

`nano /var/www/minha-api/.env NODE_ENV=production HOST=127.0.0.1 PORT=3000 DATABASE_URL=postgresql://minha_api:troque-esta-senha@127.0.0.1:5432/minha_api chmod 600 /var/www/minha-api/.env`

The app has to read `HOST` and `PORT` when it opens the port, for example with `app.listen(process.env.PORT, process.env.HOST)` in Express. If the code listens without specifying the address, Node accepts connections on every interface, and port 3000 ends up exposed if the firewall is ever turned off by mistake.

Finally, create a health route, such as `/health`, that returns 200 when the app is ready. It lets you check every deploy with a curl and feeds any external monitoring.

## Automatic restarts with PM2[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#reinicio-automatico)

Instead of a loose `pm2 start` line, describe the app in a file. That way the configuration is versioned and anyone on the team brings up the same process:

`// /var/www/minha-api/ecosystem.config.js module.exports = { apps: [ { name: "minha-api", cwd: "/var/www/minha-api", script: "dist/server.js", node_args: "--env-file=/var/www/minha-api/.env", instances: 1, exec_mode: "fork", max_memory_restart: "400M", }, ], };`

* **node\_args with env file:** Node itself loads the `.env` at startup, with no need for the dotenv library. If your code already uses dotenv, you can remove this line.
* **max\_memory\_restart:** restarts the process if it goes over the limit, containing memory leaks before they affect the rest of the VPS.
* **instances and exec\_mode:** Node uses one core per process. With more vCPUs, cluster mode with `instances: "max"` starts one process per vCPU, as long as the app keeps no state in memory, such as sessions, which would need to move to Redis.

One detail trips up a lot of people: if `package.json` has `"type": "module"`, Node treats every `.js` file in the project as an ES module, and the `module.exports` in the PM2 file fails to load. In that case, save the file as `ecosystem.config.cjs`, which PM2 also accepts, and use that name in the commands below.

Start the app and register PM2 with systemd:

`pm2 start /var/www/minha-api/ecosystem.config.js curl -i http://127.0.0.1:3000/health pm2 save pm2 startup systemd # copy and run the sudo line the command above prints pm2 install pm2-logrotate`

`pm2 save` writes the current process list, and `pm2 startup` creates the service that restores that list at boot. Without both, the app goes down on the first VPS reboot and does not come back. Test it for real with `sudo reboot` before calling it done. The differences between PM2 and plain systemd are in the guide on [PM2 and systemd on a VPS](https://streethosting.com.br/en/guides/vps/pm2-vs-systemd-nodejs).

### Keeping an eye on the app day to day[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#monitorar)

`pm2 status` shows uptime, memory and restart count for each process. `pm2 monit` opens a terminal dashboard with CPU and logs in real time. Neither of them warns you when the whole VPS becomes unreachable, though. For that, set up external uptime monitoring that hits the `/health` route through the domain every minute and sends an alert when it fails. It complements the PM2 logs and measures what the user actually sees, including DNS, certificate and Nginx.

### Updating the app[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#atualizar-versao)

`cd /var/www/minha-api git pull origin main npm ci && npm run build && npm prune --omit=dev # plain JavaScript, no build step: npm ci --omit=dev pm2 reload minha-api`

In cluster mode, `pm2 reload` swaps the processes one at a time without dropping connections. In fork mode, with a single instance, it amounts to a restart, with a drop of one or two seconds. If you changed `ecosystem.config.js`, run `pm2 startOrReload ecosystem.config.js --update-env` and then `pm2 save`. For Next.js sites, the guide on [Next.js on a VPS](https://streethosting.com.br/en/guides/vps/host-nextjs-on-vps) uses the same base with builds in separate folders.

## Nginx, domain and HTTPS[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#nginx-https)

Create the A record for the domain, or for a subdomain such as `api.seu-dominio.com.br`, pointing to the VPS IP, as in the guide on [pointing a domain to a VPS](https://streethosting.com.br/en/guides/vps/point-domain-to-vps). Then configure Nginx as a reverse proxy:

`sudo apt install -y nginx sudo nano /etc/nginx/sites-available/minha-api server { listen 80; listen [::]:80; server_name api.seu-dominio.com.br; client_max_body_size 10m; location / { proxy_pass http://127.0.0.1:3000; 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; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } sudo ln -s /etc/nginx/sites-available/minha-api /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx`

The `Upgrade` and `Connection` headers allow WebSocket, used by Socket.IO and similar libraries. If the API sends events over Server Sent Events, add `proxy_buffering off` so Nginx does not hold the messages back.

Nginx waits up to 60 seconds for a response from the app before returning a 504 to the client. For a typical API that is plenty; if some route generates reports or calls slow services, tune `proxy_read_timeout` based on the real p95 of that route, with some headroom, instead of blindly raising it to minutes. Routes that take a long time are usually better handled with a queue and an asynchronous response.

Open the firewall and issue the certificate:

`sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d api.seu-dominio.com.br sudo certbot renew --dry-run`

Certbot creates the HTTPS block and the redirect, and the systemd timer renews the certificate on its own. Never open port 3000 in the firewall. The details are in the guides on the [UFW firewall](https://streethosting.com.br/en/guides/vps/ufw-firewall-ubuntu-vps) and on [SSL certificates with Nginx](https://streethosting.com.br/en/guides/vps/lets-encrypt-ssl-certificate-vps).

Behind Nginx, Express sees every request coming from 127.0.0.1. That breaks per-IP rate limiting, logs and secure cookies. Set `app.set("trust proxy", "loopback")` in Express, or `trustProxy: "127.0.0.1"` in Fastify, to trust only the local proxy.

## Common errors and how to fix them[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#erros-comuns)

| Symptom                                  | Likely cause                                        | How to fix it                                                    |
| ---------------------------------------- | --------------------------------------------------- | ---------------------------------------------------------------- |
| Error: listen EADDRINUSE                 | Another process is already using port 3000          | Check with sudo ss -tlnp and remove the duplicate process in PM2 |
| 502 Bad Gateway                          | App stopped or listening on another port            | Check pm2 status, pm2 logs and the proxy\_pass                   |
| App disappears after a reboot            | pm2 save or pm2 startup were never run              | Run both and test with a reboot                                  |
| Repeated restarts in pm2 status          | Startup error or memory limit too low               | Read pm2 logs and review max\_memory\_restart                    |
| Cannot find module after a deploy        | Dependencies not installed or build missing         | Run npm ci and the build before the reload                       |
| Client IP always 127.0.0.1               | Framework not trusting the proxy                    | Enable trust proxy for loopback only                             |
| WebSocket does not connect               | Upgrade headers missing in Nginx                    | Add Upgrade and Connection to the location block                 |
| module is not defined in ES module scope | PM2 file saved as .js in a project with type module | Rename it to ecosystem.config.cjs                                |
| 504 Gateway Timeout                      | Route slower than the Nginx timeout                 | Optimize the route or adjust proxy\_read\_timeout                |

Almost every diagnosis starts at `pm2 logs minha-api`. If PM2 shows the app online and the error persists, the answer is in `/var/log/nginx/error.log`. A restart count that keeps climbing in `pm2 status` is a sign of a hidden problem, even if the site looks up.

## Ryzen or Xeon for Node.js[](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#ryzen-ou-xeon)

Node runs the JavaScript of each process on a single thread, so the processor clock determines how long each request occupies the event loop. That is why the [Ryzen 9 9950X VPS](https://streethosting.com.br/en/vps/ryzen), with up to 5.7 GHz and DDR5, is the main recommendation, especially for heavy WebSocket, server-side rendering and compute-heavy routes. I/O-bound APIs, which spend most of their time waiting on the database and external services, with many concurrent connections, also do well on the Xeon line, which delivers more vCPUs for the money in cluster mode. Compare the two on the [VPS plans](https://streethosting.com.br/en/vps) page.

| Scenario                                 | Suggested plan                      | Monthly price |
| ---------------------------------------- | ----------------------------------- | ------------- |
| Light API, webhooks or integration       | Ryzen 1 vCPU, 2 GB DDR5, 20 GB NVMe | R$ 40.00      |
| API with the database on the same VPS    | Ryzen 2 vCPU, 4 GB DDR5, 40 GB NVMe | R$ 66.00      |
| API with traffic, WebSocket and Redis    | Ryzen 4 vCPU, 8 GB DDR5, 80 GB NVMe | R$ 118.00     |
| Many I/O-bound connections, tight budget | Xeon 3 vCPU, 4 GB DDR4, 40 GB NVMe  | R$ 43.00      |

Every plan is in São Paulo, with Anti-DDoS included, NVMe storage, root access and activation within 60 seconds. If the app grows, upgrading through the control panel charges only the prorated difference and requires a VM reboot; since PM2 is registered at boot, the app comes back on its own.

Record the p95 latency before and after the deploy. A concrete number is more convincing than "it feels fast", and it gives you a baseline for deciding the next upgrade.

* Node 24 LTS from NodeSource, same version as in development
* App listening only on 127.0.0.1
* Passwords in .env with permission 600, outside Git
* pm2 save and pm2 startup run and tested with a reboot
* PM2 log rotation installed
* /health route returning 200
* Nginx with HTTPS, trust proxy configured and UFW active
* Alert if PM2 restarts more than 3 times per hour

In this guide

* [Why a VPS in Brazil](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#por-que-vps-local)
* [Recommended stack](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#stack-recomendada)
* [Install Node.js and PM2](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#instalar-node)
* [Step-by-step deploy](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#deploy-passo-a-passo)
* [Automatic restarts with PM2](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#reinicio-automatico)
* [Nginx, domain and HTTPS](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#nginx-https)
* [Common errors](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#erros-comuns)
* [Ryzen or Xeon for Node.js](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps#ryzen-ou-xeon)

## Frequently asked questions

Is 2 GB of RAM enough for a small Node.js API?

Yes, for Express or Fastify APIs with few dependencies and moderate traffic. Leave headroom if you run Prisma, Redis or workers on the same VPS, and set a memory limit in PM2 so a leak restarts the process before it affects the rest of the system.

Do I need Docker to run Node.js on a VPS?

Not necessarily. PM2 with Nginx handles one or a few services well. Docker helps when you have several microservices, different Node versions on the same machine or a CI pipeline that already builds images.

Can I keep the API and the database on the same VPS?

For an MVP and small projects, yes, with the database listening only on 127.0.0.1. When disk load and the number of connections grow, moving the database to a second VPS keeps an API spike from taking down the database, and the other way around.

PM2 or systemd to keep Node.js running?

Both restart the process and start it at boot. PM2 is more convenient for Node: cluster mode, zero-downtime reload and logs with a few commands. systemd needs no extra dependency. In this guide, PM2 manages the app and systemd starts PM2 at boot.

Why host the Node.js app in São Paulo?

Because every request makes a round trip to the server. For Brazilian users, apps and integrations, such as PIX and webhooks from local services, a server in São Paulo cuts tens of milliseconds per request compared to US regions, without changing a line of code.

Next step

See Ryzen VPS

Ryzen 9 9950X VPS in São Paulo with root access, NVMe and gamer Anti-DDoS.

[See Ryzen VPS](https://streethosting.com.br/en/vps/ryzen)

[See VPS plans Root VPS in Brazil with NVMe and Anti-DDoS.](https://streethosting.com.br/en/vps)

## Related guides

[VPS Intermediate PM2 vs systemd: keep your Node app always online on a VPS Your app needs to come back on its own after a crash and start together with the VPS. PM2 and systemd solve this in different ways. Here is how to set up each one and which to pick. 3 min Read guide](https://streethosting.com.br/en/guides/vps/pm2-vs-systemd-nodejs) [VPS Intermediate How to set up Nginx as a reverse proxy on a VPS Your app runs on an internal port and you want to serve it on a domain with HTTPS. Nginx as a reverse proxy solves that and also brings several apps together in one place. 3 min Read guide](https://streethosting.com.br/en/guides/vps/nginx-reverse-proxy-vps) [VPS Intermediate How to deploy FastAPI on a VPS with Gunicorn and Nginx A production-ready FastAPI API on an Ubuntu 24.04 VPS: Python in a virtual environment, Gunicorn managing Uvicorn workers through the current package, a systemd service with zero-downtime reload, Nginx with HTTPS, and how to size the workers. 9 min Read guide](https://streethosting.com.br/en/guides/vps/host-fastapi-on-vps)

[← Back to the Guide Center](https://streethosting.com.br/en/guides)
