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.
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:
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!