Running a Node.js Server as a Service Using Forever

July 7th, 2011 Permalink

There are a wide range of methods to set up a Node.js process as a service on Linux, with little consensus on a standard at this point. That's fine - Node.js is still a long way away from staid maturity and the era in a technology's development in which people settle on the two or three standard ways of solving a given problem. Here, I'll outline one of my presently preferred ways of setting up a Node.js server as a service on Linux, using Forever, an init.d script and some tailoring of the server application itself. The chief advantage of this method is that it allows for a graceful shutdown rather than just killing the process.

For later arrivals: note that this was originally written back in the archaic days of Node.js 0.4.* versions. The world changes! Some fixes to the original post were provided in January 2012 by Ben Atkin, and much appreciated. Further updates were made in July 2012.

Update as of 2/10/2013: The scripts here are for RPM-based distributions like Fedora, and are also probably over-complex for what they set out to do (e.g. see the sed parsing of Forever output). You might take a look at a set of more recent and simpler scripts for Ubuntu that can be easily adapted to RPM-based Linux as well.

Install Node.js on Linux

When installing Node.js for a server application, the two things to bear in mind are that (a) you really don't want to run any process as root if you don't have to, and (b) you have to launch a process as root in order to bind to privileged ports like 80 and 443. Well, point (b) isn't strictly true, as there are other ways to do this, but launching as the root user and then downgrading the process permissions to run as another (non-privileged) user after the port is bound is an easy method that will just work across a broad range of Linux variants.

Note that these instructions were written for installation of Node.js on Fedora.

So when installing Node.js, I first create a user that will own the running server process and the data. I'll add it to the sudo group for the sake of convenience during development, but that's probably not a good idea on a production system:

useradd -m -d /home/node node
usermod -a -G sudo node

Now switch to the newly created node user:

su - node

Now install node.js by following the standard instructions to build from source. You'll want to have the node.js source downloaded and unpacked (or checked out from Github) into /home/node/node and owned by the node user.

cd node
export JOBS=2
mkdir ~/local
./configure --prefix=$HOME/local/node
make
make install

Lastly, you will need to add the node executables to the PATH environment variable and set the NODE_PATH such that Node.js can see modules in the main node_modules library folder. You should probably set this up for all users, which can be done as follows.

Create the file /etc/profile.d/node.sh containing the following lines:

export PATH=/home/node/local/node/bin:$PATH
export NODE_PATH=/home/node/local/node/lib/node_modules

Depending on your setup, you may instead need to create the file /etc/profile.d/node.csh containing the following lines:

setenv PATH /home/node/local/node/bin:$PATH
setenv NODE_PATH /home/node/local/node/lib/node_modules

From here you'll no doubt be putting in place various necessary packages for your project, and so forth. That should all happen as the node user, and at a minimum you will need Forever, which will need to be installed globally through NPM:

npm -g install forever

Tailor Your Node.js Application

Firstly, your Node.js server application will have to downgrade its own permissions after it binds to all needed privileged ports. Your code should expect to launch under ownership by root, and alter its own permissions to run under the node user. Here is a trivial HTTP server in Express as an example:

var express = require("express");
var server = express.createServer();
var serverPort = 80;
var nodeUserGid = "node";
var nodeUserUid = "node";

server.listen(serverPort, function() {
  process.setgid(nodeUserGid);
  process.setuid(nodeUserUid);
});

Secondly, your server will need a way to listen for notices telling it to prepare for shutdown and respond when ready to shutdown. One way of doing this is to set up an HTTP server on a port only open to localhost, much as follows:

var server = express.createServer();
server.listen(10080);

server.get('/prepareForShutdown', function(req, res) {
  if(
    req.connection.remoteAddress == "127.0.0.1"
    || req.socket.remoteAddress == "127.0.0.1"
    // 0.4.7 oddity in https only
    || req.connection.socket.remoteAddress == "127.0.0.1"
  ) {
      managePreparationForShutdown(function() {
      // don't complete the connection until the preparation is done.
      res.statusCode = 200;
      res.end();
    });
  } else {
    res.statusCode = 500;
    res.end();
  }
});

var managePreparationForShutdown = function(callback) {
  // perform all the cleanup and other operations needed prior to shutdown,
  // but do not actually shutdown. Call the callback function only when
  // these operations are actually complete.
};

Thirdly, you will need to include a script with your application that will trigger the shutdown preparation. A simple method like the one above could be triggered via curl, but you may include more security measures in its use, or otherwise prefer to use Node.js:

#!/usr/bin/env node

/*
 * Tell the server to gracefully prepare for shutdown, but do not end the process.
 */

console.log("Instructing server to prepare for shutdown");

var http = require("http");

var options = {
  host: "localhost",
  port: 10080,
  path: "/prepareForShutdown",
  method: "HEAD"
};

var request = https.request(options, function(response) {
  console.log("Server completed preparations for shutdown");
});
request.end();
request.on("error", function(error) {
  throw error;
});

Finally, your application should probably log to stdout (e.g. using console.log), as it will be running under a monitor that will redirect stdout to a log file. You can manage your own logging if you want, but why do extra work when it isn't necessary?

Set up an init.d Script for Forever

It is easy enough to use Forever to manage a Node.js server as a service via an init.d script. The trick is the graceful shutdown, as forever.js simply kills processes when used in this way. The following script and setup instructions are good for a Red Hat, Fedora, or similar Linux distribution, though you will have to change the paths to suit your application and installation details. For example it assumes that your application is packaged with scripts in /scripts/start.js and /scripts/prepareForStop.js to start the server and prepare for shutdown respectively.

#!/bin/bash
#
# Service script for a Node.js application running under Forever.
#
# This is suitable for Fedora, Red Hat, CentOS and similar distributions.
# It will not work on Ubuntu or other Debian-style distributions!
#
# There is some perhaps unnecessary complexity going on in the relationship between
# Forever and the server process. See: https://github.com/indexzero/forever
#
# 1) Forever starts its own watchdog process, and keeps its own configuration data
# in /var/run/forever.
#
# 2) If the process dies, Forever will restart it: if it fails but continues to run,
# it won't be restarted.
#
# 3) If the process is stopped via this script, the pidfile is left in place; this
# helps when issues happen with failed stop attempts.
#
# 4) Which means the check for running/not running is complex, and involves parsing
# of the Forever list output.
#
# chkconfig: 345 80 20
# description: my application description
# processname: my_application_name
# pidfile: /var/run/my_application_name.pid
# logfile: /var/log/my_application_name.log
#

# Source function library.
. /etc/init.d/functions

NAME=my_application_name
SOURCE_DIR=/path/to/my/application/package/scripts
SOURCE_FILE=start.js

user=node
pidfile=/var/run/$NAME.pid
logfile=/var/log/$NAME.log
forever_dir=/var/run/forever

node=node
forever=forever
sed=sed

export PATH=$PATH:/home/node/local/node/bin
export NODE_PATH=$NODE_PATH:/home/node/local/node/lib/node_modules

start() {
  echo "Starting $NAME node instance: "

  if [ "$foreverid" == "" ]; then
    # Create the log and pid files, making sure that
    # the target use has access to them
    touch $logfile
    chown $user $logfile

    touch $pidfile
    chown $user $pidfile

    # Launch the application
    daemon --user=root \
      $forever start -p $forever_dir --pidFile $pidfile -l $logfile \
      -a -d $SOURCE_DIR/$SOURCE_FILE
    RETVAL=$?
  else
    echo "Instance already running"
    RETVAL=0
  fi
}

stop() {
  echo -n "Shutting down $NAME node instance : "
  if [ "$foreverid" != "" ]; then
    $node $SOURCE_DIR/prepareForStop.js
    $forever stop -p $forever_dir $id
  else
    echo "Instance is not running";
  fi
  RETVAL=$?
}

if [ -f $pidfile ]; then
  read pid < $pidfile
else
  pid=""
fi

if [ "$pid" != "" ]; then
  # Gnarly sed usage to obtain the foreverid.
  sed1="/$pid]/p"
  sed2="s/.*[([0-9]+)].*s$pid.*/1/g"
  foreverid=`$forever list -p $forever_dir | $sed -n $sed1 | $sed $sed2`
else
  foreverid=""
fi

case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  status)
    status -p ${pidfile}
    ;;
  *)
    echo "Usage:  {start|stop|status}"
    exit 1
    ;;
esac
exit $RETVAL

Copy your script into /etc/rc.d/init.d/my_application_name, and set its permissions appropriately. You can then set it to run as a service using a tool such as chkconfig:

/sbin/chkconfig --level 345 my_application_name on
/etc/init.d/my_application_name start

Note that Forever acts as a process monitor: it will restart your Node.js server process if it stops for any reason. If you are using another process monitor (such as the ever popular Monit), you might want to disable that aspect of Forever. You can do that via the -m option:

# Launch the application
daemon --user=root \
  $forever start -p $forever_dir --pidFile $pidfile -m 0 -l $logfile \
  -a -d $SOURCE_DIR/$SOURCE_FILE