Using Runit to supervise Nodejs Applications

Keeping Nodejs applications running in the background requires daemonizing the process and ensuring it restarts should the application encounter an error while running. To achieve this, nodejs process monitors like forever, or pm2 are used to start and supervise nodejs applications. However this comes at a very high system memory cost.

If you have ever used this setup, you'll realized that process monitors can quickly take up a lot of memory resource, making it difficult to run multiple applications in a shared server resource environment.

One of the primary reasons nodejs process monitors use a lot of system memory is because they are also nodejs applications themselves. Therefore they translate to effectively running two nodejs applications in the place of one.

If resource management is an concern for you, using your computers initialisation system provides a less memory intensive approach to keeping your nodejs applications running.

Debian based Linux distributions typically use [systemd] (https://en.wikipedia.org/wiki/Systemd) to start up processes and applications on your server or Linux computer. It is an initialisation system or init for short. It can be really powerful but can also be complex to configure.

Venturing into the /etc/init.d/ directory - which is where configuration scripts for init are kept - and going through the files there (which are executed when systemd tries to launch a service) would reveal often lengthy and daunting bash script constructions. As an example, the init script for the MySQL daemon is 191 lines of bash script. That is alot of lines just to start an application.

This is a common theme amongst many of the init scripts. Enter runit, an alternative init system.

Runit is described as a:

Unix Init Scheme with process supervision

You can replace systemd with runit but this article would show you how to use runit alongside systemd for starting and supervising nodejs applications.

Why Runit ?

In addition to its simplicity or as an extension to it, imagine a VPS somewhere in the cloud with some services or applications running but not daemonized ( e.g. being run by systemd) and then this VPS is needing a restart. Since the services were not being daemonized they would not start back once your server has rebooted. Runit makes it a breeze to ensure that those services and applications get started at boot.

Installation

Runit can easily be gotten via this command:

sudo apt-get install runit  

As at the time of writing, the latest version of on its website is 2.1.2 and this is also the version available in most current ubuntu and debian releases. But for the benefit of systems running lower versions of Debian or Ubuntu which might not have the latest versions of runit in the repositories, the manual install process is outlined below (P.S. Root access is required).

# Runit is normally installed in a package directory.
mkdir /package

# get the runit package and install
cd /package  
wget http://smarden.org/runit/runit-2.1.2.tar.gz  
tar xvf runit-2.1.2.tar.gz  
cd admin/runit-2.1.2

# run this to install
package/install

# optional if  man pages are needed (recommended)
packages/install-man  

Configuration

Before putting runit in action some steps of configuration is required. Since the goal is not to replace systemd but rather to run alongside it, runit would need to be installed as a system service. As described on the website.The stage 2 script of runit would need to be copied into one of the $PATH directories and then a /service directory would also need to be created. Finally runit would need to be setup as a systemd service.

# create the /service dirrectory
mkdir -p /service

# install the stage 2 script to a 
# location on the system path
install -m0750 /package/admin/runit-2.1.2/etc/2 /sbin/runsvdir-start

# create the service file which would let
# systemd manage runit

cat >/etc/systemd/system/runit.service <<\EOT  
[Unit]
Description=A process supervising daemon

[Service]
Type=simple  
ExecStart=/sbin/runsvdir-start

[Install]
WantedBy=multi-user.target  
EOT

systemctl enable runit.service  
systemctl start runit.service  

To confirm that runit is up and running:

ps aux |grep runsv  

Runit in Action

Now that runit is up and running (pardon the word play), Let's demonstrate its capabilities as a process manager.

We'll use runit to replace typical nodejs application monitors (forever, pm2, etc). say you have a fictitious node application running in a /var/www/node-app directory with file structure below:
node-app

With the prerequistes in place, the first step would be to create a service configuration directory.

mkdir /etc/sv  

This directory is different from the /service directory created earlier. The best analogy would be the difference between the /etc/apache2/sites-available and /etc/apache2/sites-enabled directories. The /service directory contains actively monitored processes while in this case the /etc/sv/ directory contains available processes for monitoring.

For the nodejs application a directory with the same name as the application would be created in the service configuration directory.

cd /etc/sv  
mkdir node-app  

In this newly created directory, a run file is created and made executable.

cd node-app  
touch run  
chmod a+x run  

The run file is very important when configuring a runit service. It contains information required by runit to run the process. In this example, the run file would contain

#! /usr/bin/env bash
cd /var/www/node-app

exec chpst /usr/local/bin/node /var/www/node-app/app.js  

The file above simply instructs runit to use the node exexutable to run var/www/node-app/app.js. Typically how a nodejs application would be run.

With these configurations done, to get runit to start monitoring the process, a symbolic link is created from the /etc/sv directory to the /service directory.

ln -s /etc/sv/node-app /service  

And within 5 seconds, runit would begin managing the process!. It is possible to manage the running service via the following commands:

# start
sv start node-app

# stop
sv stop node-app

# restart
sv restart node-app  

You can remove the process from runit's supervision by deleting the symbolic link from the /service directory.

rm /service/node-app  

The memory footprint is close to zero when compared to pm2 and forever. It's almost non-existent!

Is memory management a concern for you in your nodejs applications? You should try runit. If you are thinking of trying it out, or have other ideas, or stuck, feel free to use the comments sections below.

Kachifo!

Obinna Stanley Agba

Read more posts by this author.

Subscribe to Mobnia

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!