Host a Node.js app on a VM with Nginx and HTTPS
A full walkthrough: create an Ubuntu VM, lock it down, run your app as a service behind Nginx, point a domain at it and add a free certificate.
App Platform is the quickest way to run a web app, but sometimes you want the whole server: your own processes, cron jobs and full control. This is the classic setup, step by step.
Step 1: create the VM
Open Virtual Machines → Create, pick an Ubuntu image and a size, choose your SSH key, and turn on scheduled backups. Or from a terminal:
bn vm create --name web-1 --image ubuntu-24.04 --plan small --ssh-key laptop --wait
bn vm ssh web-1
Step 2: firewall
On the VM’s Firewall tab, use Quick setup to allow SSH, HTTP and HTTPS and drop everything else. Your app’s own port (for example 3000) stays closed to the internet; only Nginx will talk to it.
Step 3: install Node.js and your app
Connect as the bytenests user and install Node.js from your distribution or the NodeSource packages, then fetch your code:
sudo apt update && sudo apt install -y nginx git nodejs npm
git clone https://github.com/acme/my-app.git ~/my-app
cd ~/my-app && npm ci --omit=dev
Step 4: run it as a service
A systemd service starts the app at boot and restarts it if it crashes.
[Unit]
Description=my-app
After=network.target
[Service]
User=bytenests
WorkingDirectory=/home/bytenests/my-app
Environment=NODE_ENV=production PORT=3000
ExecStart=/usr/bin/node server.js
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now my-app
curl -I http://127.0.0.1:3000
Step 5: Nginx in front
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Step 6: point the domain
Add an A record for app.example.com with the VM’s public IP address. If the domain is registered with ByteNests, do it in Domains → Manage → DNS records; otherwise at your DNS provider. Wait until ping app.example.com returns the VM’s address.
Step 7: free HTTPS
Certbot gets a free Let’s Encrypt certificate, updates the Nginx config and renews it automatically.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com
Open https://app.example.com and your app is live.
Before each deploy
Take a snapshot from the VM’s Backups tab before upgrades. Then git pull, npm ci, and sudo systemctl restart my-app. If something breaks, Roll back the snapshot. When you outgrow this setup, App Platform does all of the above for you.