How to Host Node.js on an Ubuntu Server Using Nginx

Learn how to host Node.js on an Ubuntu server: install Nginx, deploy your project, run it with PM2, and secure it with a free Let's Encrypt SSL certificate.

This guide walks you through Host Node.js on an Ubuntu from start to finish — installing Nginx and Node, deploying your project, keeping it alive with PM2, routing traffic through Nginx, and locking it all down with a free SSL certificate.

By the end, you’ll have a Node.js app running on your own domain over HTTPS, staying online through crashes and reboots.

Use this link and get 20% Flat Discount on Hostinger Yearly Plan 

Prerequisites

  • An Ubuntu server (22.04 LTS or 24.04 LTS) with SSH access— if you don’t have one yet, see our guide to setting up an Ubuntu server first.
  • A non-root user with sudo privileges
  • A domain name with its DNS A record already pointed at your server’s IP address (required for Step 7). check you DNS here.
  • Your Node.js project ready to deploy, either in a Git repository or on your local machine

Step 1: Install Nginx and Configure the Firewall

We set up Nginx and the firewall first so the server has a working, secure foundation before anything else goes on it.

Update your package list, then install Nginx:

				
					sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx
				
			

Confirm it’s running:

				
					sudo systemctl status nginx
				
			

Now configure UFW (Ubuntu’s firewall). Always allow SSH first so you don’t lock yourself out:

				
					sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
				
			

“Nginx Full” opens both port 80 (HTTP) and 443 (HTTPS), so you’re already prepared for SSL later.

Visit your server’s IP address in a browser — you should see the default “Welcome to nginx!” page, confirming everything’s working before we add anything else.

Step 2: Install Node.js

We recommend installing Node with “nvm” rather than a fixed version number — it lets you install and switch Node versions easily and won’t go stale as new LTS releases ship.

Install nvm:

				
					curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
				
			

Install the latest LTS release of Node.js:

				
					nvm install --lts
nvm use --lts
				
			

Verify:

				
					node -v
npm -v
				
			

Alternative: if you’d rather install Node as a system-wide package, use the NodeSource repository instead:

				
					curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
				
			

Step 3: Upload Your Node.js Project to the Server

We’ll deploy the project into /var/www/my-node-app — the standard location Nginx configs typically expect. Since /var/www is owned by root by default, create the project folder and hand ownership to your user first so you don’t need sudo for every upload or git pull:

				
					sudo mkdir -p /var/www/my-node-app
sudo chown -R $USER:$USER /var/www/my-node-app
				
			
Now pick whichever of the two options below fits your workflow.

Option A: Upload via SFTP

Using an SFTP client like FileZilla or WinSCP, connect to your server with your SSH credentials, navigate to /var/www/my-node-app, and upload your project’s files there.

If you prefer the command line, scp handles this in one step (the trailing /. copies the folder’s contents rather than the folder itself):

				
					scp -r /path/to/local/my-node-app/. your_user@your_server_ip:/var/www/my-node-app/
				
			

Option B: Deploy via Git

If your project is in a Git repository, clone it directly into the folder you just created:

				
					git clone https://github.com/your-username/your-repo.git /var/www/my-node-app
				
			

(Cloning into an existing empty directory works fine — Git will populate it in place.)

For ongoing deployments, many teams set up a GitHub Action that SSHes into the server and runs “git pull” (plus a PM2 restart) on every push to main. That’s a deeper topic worth its own guide — for now, a manual “git pull” after each update works fine:

				
					cd /var/www/my-node-app
git pull origin main
				
			

Step 4: Install npm Packages and Test the App

Move into your project folder and install dependencies:

				
					cd /var/www/my-node-app
npm install
				
			

Start the app to confirm it runs cleanly:

				
					node app.js
				
			

(Replace app.js with your actual entry file — check package.json for the main field or start script if you’re unsure.)

In a separate terminal session, test it locally:

				
					curl http://localhost:3000
				
			

Replace 3000 with whatever port your app listens on. If you get a response back, your app is working.

Stop the app with Ctrl+C — next we’ll hand it off to PM2 so it doesn’t depend on this terminal session staying open.

Step 5: Install PM2 and Run the App with PM2

Install PM2 globally:

				
					npm install -g pm2
				
			

Start your app under PM2 instead of running it directly with node:

				
					pm2 start app.js --name my-node-app
				
			

Check that it’s running:

				
					pm2 status
				
			

Useful commands going forward:

				
					pm2 logs my-node-app     # view real-time logs
pm2 restart my-node-app  # restart after code changes
pm2 stop my-node-app     # stop the app
				
			

Auto-Start PM2 on Reboot

				
					pm2 startup
				
			

Run the command it prints out exactly as shown, then save the current process list:

				
					pm2 save
				
			

Your app will now survive both crashes and full server reboots.

Step 6: Set Up Nginx Reverse Proxy and Run the App on Your Domain

Now we connect the dots: Nginx will listen on port 80/443 and forward requests to your app running on its internal port (e.g. 3000).

Create a new site config:

				
					sudo nano /etc/nginx/sites-available/my-node-app
				
			

Paste in the following, replacing your_domain.com with your actual domain and 3000 with your app’s port:

				
					server {
    listen 80;
    server_name your_domain.com www.your_domain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
				
			

Enable the config and remove the default one so it doesn’t conflict:

				
					sudo ln -s /etc/nginx/sites-available/my-node-app /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
				
			

Test the config, then reload Nginx:

				
					sudo nginx -t
sudo systemctl reload nginx
				
			

Visit http://your_domain.com in a browser — your app should now load on your actual domain, no port number needed.

Step 7: Install a Let's Encrypt SSL Certificate

With DNS already pointed at your server (from the prerequisites) and Nginx correctly serving your domain, adding HTTPS takes just a couple of commands via Certbot.

Install Certbot and its Nginx plugin:

				
					sudo apt install -y certbot python3-certbot-nginx
				
			

Request and install the certificate:

				
					sudo certbot --nginx -d your_domain.com -d www.your_domain.com
				
			

Certbot will ask for an email (for renewal notices) and whether to redirect HTTP traffic to HTTPS — choose yes, it’s the recommended default. It automatically edits your Nginx config to add the SSL block.

Let’s Encrypt certificates expire every 90 days, but Certbot sets up automatic renewal for you. Confirm it’s working with a dry run:

				
					sudo certbot renew --dry-run
				
			
Troubleshooting Tips
  • 502 Bad Gateway — your Node app likely isn’t running. Check pm2 status and pm2 logs my-node-app.
  • Certbot fails to obtain a certificate — almost always means your domain’s DNS A record isn’t pointing at the server yet, or hasn’t propagated. Verify with dig your_domain.com.
  • Nginx won’t reload after editing config — run sudo nginx -t first; it points to the exact line with the error.
  • App changes not showing up — after editing code, run pm2 restart my-node-app. PM2 doesn’t watch files by default.
  • Can’t connect at all — check sudo ufw status, and if you’re on a cloud provider like AWS, also confirm the security group allows inbound traffic on ports 80 and 443.

ON THIS PLACE