Hosting Websites on Bare Minimum VPS/Dedicated Servers

How To Set Up a Node.js Application for Production on a CentOS 7 VPS

Tags: , , , , , Date/Time: June 10, 2019 @ 2:09 pm, by Dusko Savic

In this tutorial, we will create a simple Node.js application and put it into a production ready environment. We are going to install and use the following pieces of software:

  • Nginx as a reverse proxy. It will make the app accessible from your browser, and in case you ran several sites from the same server, it could serve as a load balancer as well.
  • Certbot will let us install Let’s Encrypt certificates. Access to the site will be secure as only the HTTPS requests will be honored.
  • NPM package called PM2 will turn a node.js app into a service. The app will run in the background, even after system crashes or reboots.

What We Are Going To Cover

  • Install Nginx
  • Install firewall-cmd and enable rules for 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, 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

Prerequisites

We use Centos 7:

  • 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:
yum install nano

Step 1: Install Nginx

After you have logged in as a root user, you will install Nginx. Add the CentOS 7 EPEL repository with this command:

yum install epel-release

Next, install Nginx:

yum install nginx

Press ‘y’ twice and the installation will be finished. Enable Nginx service to start at server boot:

systemctl enable nginx

Step 2: Change Firewall Rules to Enable Nginx

Let’s now install firewall-cmd, the command line front-end for firewalld (firewalld daemon), for CentOS. It supports both IPv4 and IPv6, firewall zones, bridges and ipsets, allows for timed firewall rules in zones, logs denied packets, automatically loads kernel modules, and so on.

Install it in the usual manner, by using yum:

yum install firewalld

Let us now start it, enable it to auto-start at system boot, and see its status:

systemctl start firewalld
systemctl enable firewalld
systemctl status firewalld

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 the 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:

firewall-cmd --permanent --zone=public --add-service=ssh
firewall-cmd --zone=public --add-port=3000/tcp --permanent
firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --reload

Let us now start Nginx:

systemctl start nginx

With HTTP functioning, we can visit this address in the browser:

http://YOUR_DOMAIN/

and verify that Nginx is running:

Step 3: Install Latest Node.js

We’ll now install the latest release of Node.js. First, install development tools to build native add-ons (make, gcc-c++) and then enable Node.js yum repository from the Node.js official website:

yum install -y gcc-c++ make
curl -sL https://rpm.nodesource.com/setup_12.x | sudo -E bash -

Now, the repository is added to your VPS and we can install the Node.js package. NPM, the package manager for Node.js, will also be installed, as well as many other dependent packages in the system.

yum install nodejs

Press ‘y’ twice to finish the installation. Show the version of Node.js that is installed:

node -v

It shows v12.3.1, which was the actual version 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. Since our app will turn any input into uppercase letters, we first 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');
console.log('starting...');
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'));
  res.end();
}).listen(8080);

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 firewalld).

Run the app:

node uppercase-http.js

You will see the following message:

starting...

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/test

The program correctly converted path to uppercase. The server app shows a status message for the request:

received request for url: /test

Now we have two terminal windows, one with the app running and the other which we used to test the app. The first window is blocked as long as the app is running and we can press Ctrl-C from keyboard to stop it. If we do so, the app won’t be running later when we access it from the browser. The solution is to either activate the app again or — much cleaner — enter further commands only into the second terminal window for the rest of this tutorial.

Step 6: Configure Nginx as Reverse Proxy

Nginx for CentOS comes without folders for available and enabled sites, as is the custom on Ubuntu Linux. You’ll need to create them:

mkdir /etc/nginx/sites-available
mkdir /etc/nginx/sites-enabled

Then, edit Nginx global configuration to load config files from these folders:

nano /etc/nginx/nginx.conf

Find line

include /etc/nginx/conf.d/*.conf;

and insert these lines:

include /etc/nginx/sites-enabled/*;
server_names_hash_bucket_size 64;

Save and close the file. Now Nginx will read the contents of the “enabled” sites.

For the sake of completness, our nginx.conf file looks like this:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
    server_names_hash_bucket_size 64;

You may want to copy it and paste it.

With NPM package nginx-generator we 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 \
      /etc/nginx/sites-enabled/site_nginx

Replace YOUR_DOMAIN with your actual 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 name instead of site_nginx for the file.)

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

Run the app again:

node uppercase-http.js

You will see the following message:

starting...

In your browser, go to address

http://YOUR_DOMAIN/test

and the result should be

/TEST

Bad Gateway Case No. 1 – The App is Not Active

Instead of proper result, which in this particular case would be text printed in uppercase letters, it is all too easy to get the message Bad Gateway in this place.

The main reason is that we were using one teminal window to both run the app and insert other commands. When you start the app with node uppercase-http.js, it will block the entire window and when you want to try out the next command in the installation process, the app will stop running. One way to prevent this is to repeat starting the app all over again, as we have done in this tutorial.

Another way would be to open two terminal windows, start the app in one of them and then proceed with further commands in the second terminal window, exclusively.

Bad Gateway Case No. 2 – SELinux Is Active

If SELinux is enabled, it can block Nginx from making outbound connections.

You can check this with:

getenforce

If you get Enforcing as the result, SELinux is active. Run this command to let Nginx serve as a reverse proxy:

setsebool -P httpd_can_network_connect true

Step 7: Securing Your Site To Serve Only HTTPS

We want to serve our app via a HTTPS request. 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 Centos/RHEL 7 for System – it should look like this:

Certbot Certbot Site

Certbot is packaged in EPEL (Extra Packages for Enterprise Linux). To use Certbot, you must first enable the EPEL repository. On CentOS, you must also enable the optional channel, by issuing the following commands:

yum -y install yum-utils
yum-config-manager --enable rhui-REGION-rhel-server-extras rhui-REGION-rhel-server-optional

Now install the Certbot by executing this:

yum install certbot python2-certbot-nginx

It will compute the dependencies needed and ask you to let it proceed with the installation.

Press ‘y’ when asked.

Finally, run Certbot:

certbot --nginx

If you are installing certificates for the fist time, Certbot 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!

Activate Nginx as you normally would after each change in parameters:

systemctl restart nginx

To verify that redirection is working, go to the same address in your browser:

https://YOUR_DOMAIN/test

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”.

Use NPM to install PM2:

npm install pm2@latest -g

Option -g tells it to install pm2 globally, so it can run from all paths in the system.

Let’s now run our application under PM2:

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:

pm2 list

The following command

pm2 show 0

will show details of the app with ID of 0:

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

pm2 monit

Other useful PM2 commands are stop, restart, delete.

What Can You Do Next

Now you have a node.js app in a production environment, using HTTPS protocol for safety, Nginx for speed and as a reverse proxy, running as a service in the background. We have just installed one such app and one such site, while you may run serveral node.js apps and sites from the same server. We used root user throughout for ease of installation, while for multiples sites you would need multiple non-root users for safety.

Dusko Savic is a technical writer and programmer.

duskosavic.com

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. Quoting webhostingtalk.com URL seems to get binned consistently here, but I do peek into the spam box frequently to publish those comments.
  • 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 *