The Show Must Go On: Running Node Apps in Production

featureimage tl;dr: In this post I'll highlight three modules that you can use to to run your node app in production: using forever, nodemon, and PM2. So it's pm2 vs forever vs nodemon. Each has its own strengths and weaknesses: forever is the most general, nodemon the best suited for development, and PM2 is the most comprehensive and best-suited for production.

Production

Ah, Production™. That happy place where everything is supposed to go smoothly and serenely1, where programs are their supposed to be their best, most efficient and bug-less selves. These programs have, in theory, passed through the gauntlet of tests, the askance looks of the designers, having been checked off by the Product people and the UX tribunals. All is supposed to go well, perfection -- at least until you ship another iteration before lunch. Then the serenity begins all over again.

Right? Right. Well, almost right. That's the goal, anyways. But, inevitably, errors and bugs will occur. The (hopefully) extensive tests we write should winnow out the majority of these bugs, but it's a near certainty that at some point, a bug will sneak into production. It happens to the best companies in the world and to the most excellent of engineers. This, I hope, is more of a realism than a pessimism or cynicism.

Engineers don't create bugs because they're terrible engineers; engineers create bugs because they are human.

Node, like a good runtime, will complain when there's an error and stop running if the exception is an unhandled. This is expected behavior and is good and fine. But what isn't expected behavior for your users is when the gloriously beautiful web app you've crafted suddenly grinds to a halt and they're forced to see the ever-so-witty 500 page you've crafted. I tend to use DiCaprio:
Congrats, you broke something!

The Show Must Go On

So, what's to be done? If you're using clustering in node you can write some neat ways to resurrect a process or simply kill and then spawn a new one. You could also just hire hundreds of QA engineers and fuzz the bugs out of your code as much as possible, hoping that'll do the trick.

Or you could use take advantage of modules that are designed to handle this sort of thing. I'll highlight three main ones, since each has it's own use-case that it's meant for (although you could theoretically use any of them anywhere). By the time we're done, I hope you have a better idea of which of these modules might be suited to your workflow and how you might better approach running a resilient node app in production.

Forever

Forever is the classic (if such a thing can be considered 'classic') module for this sort of thing. From the README:

A simple CLI tool for ensuring that a given node script runs continuously (i.e. forever)

The idea behind this and the other modules we'll look at is pretty straightforward: you're given a way to either automatically or programmatically restart node processes given a set of circumstances you configure. The most common case is to restart in the event of an unhandled exception, but there are lots of tunable circumstances you can plan for.

Forever is pretty straightforward to use; you install it globally (npm i -g forever) and you can then invoke the forever executable on the CLI.
Basic usage:

$ forever -m 5 examples/error-on-timer.js

Note: if you want to use it programmatically, you'll need to use the forever-monitor module.

Personally, I have the least experience with forever. However, I know that it's one of the 'older' (again, if we can consider such a thing to be old) modules that handles this solution arena. (Looks like the first commit was over 5 years ago: --> 76467b2)

Nodemon

Nodemon is a "simple monitor script for use during development of a node.js app". I've found this module to be the most useful for development, since it's incredible easy to restart and has file-watching enabled and baked in by default. Again, you install it globally (npm i -g nodemon) and invoke is on the cli:

nodemon ./bin/www  

Like the other modules, you can tune what you watch with flags:

nodemon --watch app --watch libs app/server.js  

So far, some slight differences between nodemon and forever are the tunability of filewatching, which sorts of file types you want to watch, and delaying restarts. Remember, it seems like it's meant primarily for development, but you can of course use it however you please.

In my own development process, I tend to use nodemon to run the development server, especially in cases where I'm doing React development. I'll use a combination of broswerify (disclaimer: no, I haven't yet tried webpack; no, I will not get into a debate with you about the two ¯\(ツ)/¯ ), watchify (to speed up broswerify builds), and gulp to transpile my JSX and subsequently restart the server, if necessary.

PM2

Enter PM2. Of the tools mentioned so far, PM2 seems to be the most comprehensive. Now, this doesn't doesn't immediately make it either good or bad, neither better nor worse. It's up to you to decide how a given tool will fit into a workflow and whether or not a solution matches the scope of a problem.

Again, you install it globally with:

npm i pm2 -g  

Running an app is as simple as:

$ pm2 start app.js

For my purposes and in my experience, PM2 is what I use for running my node/io.js apps in production. Here are a few reasons for why I usually choose it over others:

  • auto-restart: If/when an error gets thrown, pm2 will automatically restart your node process(es) to ensure you don't have a hung server. Pro-tip: it's usually very important you specify a maximum number of restarts, otherwise you run the slight risk of a persistent error causing PM2 to restart continuously. I've walked away from my laptop and come back to find the fans cranking at the highest RPM possible -- some require() typo was causing PM2 to diligently restart my procs as fast as possible.
  • highly configureable: PM2 gives you tons of parameters to tune/act on. You get max memory usage, cron patterns, watching patterns/directories,
  • easy logs management & process monitoring: pm2 exposes a nice set of termcaps-like monitoring and makes getting logs from all or individual apps really easy. You can also do things like log rotation, management, &c.
$ pm2 logs
$ pm2 logs big-api

  • graceful restarts: If one of my apps is taking up too much memory (which hopefully never or rarely happens) or needs to restart for some other reason, there's a gracefulReload function you can call programmatically. You can also set memory usage limits and PM2 will automatically restart for you.
$ pm2 gracefulReload [all|name]
  • clustering: To keep sever/instance costs low and spread the load across processes/CPUs, I tend to use pm2's clustering module. Firing up a bunch of clusters is really easy with the JSON option (see next point).
# Start the maximum processes depending on available CPUs
$ pm2 start app.js -i 0
  • JSON-configuration: Forever also has a similar API that lets you specify a JSON file with all the parameters you want. This might not seem like a killer feature at first, but when you use Docker (or something like supervisor/foreman), it's way easier & more readable to declare processes like this than jamming everything into one line. This also lets you create a number of process lists for different environments. Finally, if you need to start a number of apps all at once, that's as easy as filling out an array.
# Start all apps
$ pm2 start processes.json
{
  "name"             : "node-app",
  "cwd"              : "/srv/node-app/current",
  "args"             : ["--toto=heya coco", "-d", "1"],
  "script"           : "bin/app.js",
  "node_args"        : ["--harmony", " --max-stack-size=102400000"],
  "log_date_format"  : "YYYY-MM-DD HH:mm Z",
  "error_file"       : "/var/log/node-app/node-app.stderr.log",
  "out_file"         : "log/node-app.stdout.log",
  "pid_file"         : "pids/node-geo-api.pid",
  "instances"        : 6, //or 0 => 'max'
  "min_uptime"       : "200s", // 200 seconds, defaults to 1000
  "max_restarts"     : 10, // defaults to 15
  "max_memory_restart": "1M", // 1 megabytes, e.g.: "2G", "10M", "100K", 1024 the default unit is byte.
  "cron_restart"     : "1 0 * * *",
  "watch"            : false,
  "ignore_watch"      : ["[\\/\\\\]\\./", "node_modules"],
  "merge_logs"       : true,
  "exec_interpreter" : "node",
  "exec_mode"        : "fork",
  "autorestart"      : false, // enable/disable automatic restart when an app crashes or exits
  "vizion"           : false, // enable/disable vizion features (versioning control)
  "env": {
    "NODE_ENV": "production",
    "AWESOME_SERVICE_API_TOKEN": "xxx"
  }
}
  • startup script generation: I haven't use this personally, as my usage of Docker obviates the need for a startup script, but it's still highly useful for other use-cases.
  • frequently/well-developed: The team behind PM2 consistently cranks out updates and releases, so I know I'm not buying into a tool that's dead/dormant and without support.
  • more: There are lots of other features in PM2, like app deployment, pulling, rollback, and more. I tend not to use those features since they seem to overlap too heavily with other tools that are better suited for those sorts of things. Maybe that's not the case for you, but I like not to build everything on one module, lest I reap the painful consequences better.

Summary

So that's a fast overview of some different modules you can use to run your node apps in production. We went over a few different modules and their various strengths and weaknesses. Forever is the classic "keep things running" module. Nodemon tends to be best for development. PM2 is by far the most comprehensive and, in my opinion, tends to be best suited for production concerns. I hope this has been helpful and will ensure a stress-free journey to the land of The Production™.

¯\_(ツ)_/¯

Thoughts, questions, ideas, tirades? Write them all in the comments below or discuss them on Hacker News!


1 Maybe not serenely, but perhaps 'blazingly elegantly'? If your app is 'serene', you might want to find more users :)