LowEndBox - Cheap VPS, Hosting and Dedicated Server Deals

How To Set Up a Node.js Application for Production on a Debian 9 VPS

In this tutorial, we will create a simple Node.js application and put it into a production ready environment on Debian 9, “Stretch”.

  • To make the app accessible from your browser, we’ll use Nginx as a reverse proxy. Nginx is a fast and versatile web server, and can also be used as a Load Balancer.
  • To make the access to the app secure, we’ll use Certbot to install Let’s Encrypt certificates. Then, we’ll be able to use HTTPS requests instead of unsecure HTTP requests.
  • Finally, we will make sure that the app is running continually, by turning it into a service with a special NPM package called PM2. PM2 will daemonize the Node.js app and run it in the background, even after system crashes or reboots.


What We Are Going To Cover

  • Install Nginx
  • First install firewall-cmd, then change its rules to enable Nginx
  • Install the latest version of Node.js
  • Add NPM packages for the app that we are making
  • Create the example app to show all characters in upper case
  • Configure Nginx as a reverse proxy
  • Install Let’s Encrypt certificates to serve HTTPS requests
  • Access the app from the browser
  • Install PM2, which is a production process manager for Node.js applications with a built-in traffic Load Balancer
  • Use PM2 to restart the Node.js app on every restart or reboot of the system


We use Debian 9:

  • Starting with a clean VPS with
  • At least 512Mb of RAM and
  • 15Gb of free disk space.
  • You will need root user access via SSH
  • A domain name pointed to your server’s IP address (it can also be a subdomain) using A records at your DNS service provider
  • We use nano as our editor of choice, and you can install it with this command:
sudo apt install nano

Step 1: Install Nginx From a Repository

After you have logged in as a root user, install Nginx. Let’s refresh the cache and start the installation:

sudo apt update
sudo apt upgrade -y

Now install Nginx:

sudo apt install nginx -y

In the browser, go to address


and verify that Nginx is running:

Step 2: Change Firewall Rules to Enable Nginx

On Debian, the iptables firewall is installed by default, but we prefer ufw, the uncomplicated firewall. First install it:

sudo apt install ufw

List all the port profiles ufw already knows about:

sudo ufw app list

That is quite a list:

There are three options fo Nginx:

  • Nginx Full: Opens ports 80 and 443 for normal, unencrypted web traffic and TLS/SSL for encrypted traffic, respectively.
  • Nginx HTTP: Only port 80 (normal, unencrypted web traffic)
  • Nginx HTTPS: Only port 443 (TLS/SSL encrypted traffic)

In theory, we should use the most restrictive type of access. However, there is a possibility that someone will use a HTTP request instead of a HTTPS, so we enable the full option:

sudo ufw allow 'Nginx Full'

Node.js apps require a port that is not used by the system, but is dedicated to that one app only. In our examples, we might use ports such as 3000, 8080 and so on, so we need to declare them explicitly, otherwise your app won’t run.

Here is a list of ports and feel free to add any other that your host requires for the normal functioning of the system:

sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 20/tcp
sudo ufw allow 21/tcp
sudo ufw allow 3000
sudo ufw allow 8080
sudo ufw allow 'Nginx Full'
sudo ufw enable


Always execute ufw commands with ssh, http, https and so on, otherwise you will NOT be able to log back into your VPS server!

To see the changes, run:

sudo ufw status

Step 3: Install Latest Node.js

Execute this series of commands to install node.js:

cd /tmp
sudo apt install curl -y
sudo curl -sLO https://deb.nodesource.com/setup_12.x
sudo bash setup_12.x
sudo apt-get install gcc g++ make
apt install nodejs
sudo nodejs -v

The script sets up the proper Debian package repositories and also installs gcc, g++ and make. They may be required by some Node.js packages during their installation to compile their parts written in C or C++. Finally, it inspects the Node.js version:

Node.js version

It shows v12.3.1, which was the actual version of at the time of this writing. If it shows an error, double check the commands you entered against the ones shown above.

Step 4: Adding NPM Packages

We of course know what packages our Node.js app will need, so we install the required npm packages in advance. First we install prerequisite packages for NPM:

sudo apt install build-essential libssl-dev

With that, we will be able to install all other packages out there.

Since our app will turn any input into uppercase letters, we install a package for that:

npm install upper-case

Most Node.js apps will now use Express.js, so let’s install that as well:

npm install --save express

Execute this command as well:

npm install -g nginx-generator

It will globally install an NPM package to generate the reverse proxy config for Nginx. We will apply it after the app is running on port 8080.

Step 5: Creating The App

Open a file named uppercase-http.js for editing:

nano uppercase-http.js

Add the following lines:

var http = require('http');
var uc = require('upper-case');
http.createServer(function (req, res) {
  console.log('received request for url: ' + req.url);
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.write(uc(req.url + '\n'));

Save and close the file.

The HTTP server will listen to port 8080. You can specify any other port that you like, provided that it will be free when the app is running (and that you have previously opened access to it in ufw).

Run the app:

node uppercase-http.js

You will see the following message:


Node.js app starting

To test it, fire up another terminal, connect to your VPS as root via SSH and curl localhost:8080:

curl localhost:8080
curl localhost:8080/test

The program correctly converted the path to uppercase. If you look at the server app itself, it showed a status message for both requests:

received request for url: /
received request for url: /test

App server messages

Now you have two windows, one with an active app and the other with the curl statement. From now on, enter commands only into the second window, otherwise you would have to stop the app and then the rest of the procedure would make no sense.

Step 6: Configure Nginx as Reverse Proxy

Let’s now access this app from your web browser. Empty the default file in folder /etc/nginx/sites-available/ with the following two commands:

rm /etc/nginx/sites-available/default
sudo nano /etc/nginx/sites-available/default

That file has to be empty in order for Nginx to read files in the /etc/nginx/sites-enabled/ folder. Enter one space into this otherwise empty file, save and close it.

Now, we will use the NPM package nginx-generator that we have installed earlier in this tutorial, to generate files that will tell Nginx to act as a reverse proxy. In the command line, execute the following:

nginx-generator \
      --name site_nginx \
      --domain YOUR_DOMAIN \
      --type proxy \
      --var host=localhost \
      --var port=8080 \

Replace YOUR_DOMAIN with your domain before running this command.

That command creates a file called site_nginx and puts it into the directory /etc/nginx/sites-enabled/. (We could have used any other file name instead of site_nginx.)

We can see it with this command:

sudo nano /etc/nginx/sites-enabled/site_nginx

Test the configuration:

sudo nginx -t

and if everything is OK, restart Nginx:

systemctl restart nginx

In your browser, go to address


and the result should be


Step 7: Securing Your Site To Serve Only HTTPS

Our goal is to run a production version of a Node.js app, which means we must serve it for HTTPS requests. If you have a domain name and DNS records properly set up to point to your VPS, you can use certbot to generate Let’s Encrypt certificates. This means that you will always access the app as well as the rest of your domain, via HTTPS.

We will folow the original documentation to install Let’s Encrypt. Choose Nginx for software and Debian 9 (stretch) for System – it should look like this:

Certbot Certbot Site

Certbot for Debian comes from the Backports repo so let us open the sources list

nano /etc/apt/sources.list.d/nginx.list

and enter the following line into that file:

deb http://deb.debian.org/debian stretch-backports main


apt-get update

and install backports:

sudo apt-get install certbot python-certbot-nginx -t stretch-backports

Finally, activate it with:

sudo certbot --nginx

What certbot will ask you will depend on the previous events. If you are installing it for the fist time, it will ask for an emergency email address, then several less important questions and finally – do you want to redirect all HTTP traffic to HTTPS? Select 2 to confirm this redirection, and you’re all set!

Test the configuration:

sudo nginx -t

and if everything is OK, restart Nginx again:

systemctl restart nginx

In your browser, go to address


and the result should be


Note that this address started with HTTP, but that it ended up as HTTPS.

Step 8: Install PM2

PM2 is a production process manager for Node.js applications. With PM2, we can monitor applications, their memory and CPU usage. It also provides easy commands to stop/start/restart all apps or individual apps.

Once the app is started through PM2, it will always be restarted after system crashes or restarts. In effect, it will “always be there”.

We use NPM to install PM2:

sudo npm install pm2@latest -g

Option -g tells it install pm2 globally, meaning that we shall be able to run it from all paths in the system.

Let’s now run our application under PM2:

cd ~
pm2 start uppercase-http.js

The output of PM2 can be spectacular when run for the first time, but we really need to concentrate on the rows about the app:

PM2 will use app name, and then show the id number of the app, mode, status, CPU usage and memory. If two or more apps are running in the background, they will all be presented in this table.

We can list the processes like this:

sudo pm2 list

The following command

sudo pm2 show 0

will show details of the app with ID of 0:

It is also possible to monitor CPU usage in real time:

sudo pm2 monit

Other useful PM2 commands are stop, restart, delete.

What Can You Do Next

We have shown how to set up a Node.js app for a production environment, as a root user, on a Debian 9 VPS. You can study more about Nginx and add several sites on one VPS server, each with its own non-root user, listening on different ports and so on. As a Node.js programmer, you would also pay attention to debugging processes and other good practices – all of which is, however, out of scope of this article.

Dusko Savic is a technical writer and programmer.


No Comments

    Leave a Reply

    Some notes on commenting on LowEndBox:

    • Do not use LowEndBox for support issues. Go to your hosting provider and issue a ticket there. Coming here saying "my VPS is down, what do I do?!" will only have your comments removed.
    • Akismet is used for spam detection. Some comments may be held temporarily for manual approval.
    • Use <pre>...</pre> to quote the output from your terminal/console, or consider using a pastebin service.

    Your email address will not be published. Required fields are marked *