NODE_OPTIONS has landed in 8.x!

This blog was originally published in the Nodejs @ IBM blog by Sam Roberts.

A new NODE_OPTIONS environment variable is available in 8.0.0, see the 8.x docs. It should land soon in a 6.x minor (most likely 6.12.0) to be available for production deployments.

NODE_OPTIONS allows command line arguments that make sense (not -e, for example) to be provided through an environment variable. As an environment variable, it can be specified without actually modifying any scripts that run node, and it affects all processes that inherit the environment, particularly node child processes.

An example of when it’s not possible to control the node invocation arguments are npm install scripts. If you were tearing your hair out trying to figure out why a sub-dependency script is failing during npm install but not when you run it standalone, you could try:

Note that I need to have node-report installed somewhere and specify a fully-qualified path to it, since its not a dependency of npm.

Once #13932 lands, this will also be possible:

While useful for local debugging, — abort-on-uncaught-exception is also an excellent troubleshooting tool for production deployments, and causing node to generate a core file on failure is a production best-practice. (See Exploring Core Dumps for information on how to use core files for post-mortem debugging.) Despite this, most clouds run node applications with npm start (there is no other common convention for running node applications, unfortunately), and most start scripts are written by developers without considering how the app should be run by in production. NODE_OPTIONS allows the DevOps responsible for production to control these aspects of the runtime so that developers don’t need to.

Here’s some background on why configuration by environment, and NODE_OPTIONS in particular, is so useful.

Node has a number of runtime configuration options, and they have 3 ways of being specified:

  1. compiled-in configuration
  2. environment variables
  3. command line options

Of these, build configuration is the least flexible, it can only be used if you compile node yourself. It’s a good mechanism for Linux distributions, for example, that build node themselves, and want node to link against the system OpenSSL libraries ( — shared-openssl) and to use the system CA store ( — openssl-use-def-ca-store).

Build configuration is not a good mechanism for anything that an end user might want to control. For that, we need to use environment variables or command line options.

Whether to use one or the other is always a problematic choice, as can be seen by looking at the node — help output. The current state is a bit of a mixed bag, some options are only available by modifying the node command line ( — zero-fill-buffers and — tls-cipher-suites), some only by modifying the environment (NODE_EXTRA_CA_CERTS), and some are available in both (NODE_NO_WARNINGS/ — no-warnings and OPENSSL_CONF/ — openssl-config).

There is a case to be made that all options should be available both via environment variable and command line option, but this can lead to a lot of duplication. There are over well over 400 options supported by the node command line at the moment, exposing each as an environment variable is not reasonable.

There is another approach, to instead expose options through the CLI, and have a single environment variable that can be used to provide CLI options. This is a common feature in scripting languages (RUBYOPT, PERL5OPT). With this pattern, all options can be specified either via environment variable (without having to invent a new environment variable specific to the option), on the command line, or both (command line will take precedence over the environment).

This approach is now available via the NODE_OPTIONS environment variable.

I’m pretty excited about this, because while a command line argument is the most convenient way to specify options for users working at a terminal prompt, it can be quite difficult to find and modify the command line invocation in production. They are often hard-coded into package.json scripts, or templates for generating systemd files. This can make it very onerous to uniformly control important features such as the trusted CA roots or default cipher suites in production. Also, container based systems favour immutable containers and configuration via environment variable, so having to modify a container to change command line options can be very difficult. Environment variables are also a powerful way of setting defaults globally, because they are inherited by child processes. They can be set machine/image wide, per-user, per command invocation (along with all the child processes of that command).

NODE_OPTIONS is not perfect, due to some nervousness this first version has gone with a whitelist approach which requires any command line option allowed in the environment to be explicitly listed in the node source. In particular, notice that most V8 flags are not permitted. We may change this to a blacklist at some point, but in the meantime if any V8 command line option you want is not supported please raise an issue on the node issue tracker and we should be able to get it supported.

*Final note: https://github.com/nodejs/node/pull/12677 contains a NODE_OPTIONS backport to 6.x, it should happen, but probably won’t get released until the next scheduled minor, 6.12.0, Nov. 7th, see here for more details.

*A thank you to technical editor Simeon Vincent for reviewing this post.