Skip to main content

Node Renderer JavaScript Configuration

Pro Feature — Available with React on Rails Pro. Free or very low cost for startups and small companies. Upgrade or licensing details →

You can configure the node-renderer entirely with ENV values from your own launch file or package.json script. The package does not ship a standalone node-renderer CLI.

For most apps, create a small configuration file to set up and launch the node-renderer.

The values in this file must be kept in sync with the config/initializers/react_on_rails_pro.rb file, as documented in Configuration.

Here are the options available for the JavaScript renderer configuration object, as well as the available default ENV values if you wire them into your own launch script.

  1. port (default: process.env.RENDERER_PORT || 3800) - The port the renderer should listen to. On Heroku or ControlPlane you may want to use process.env.PORT.
  2. host (default: process.env.RENDERER_HOST || 'localhost') - The host/IP address the renderer should bind to. Set it to 0.0.0.0 for containerized environments where external health checks need to reach the server. Security caution: binding host to 0.0.0.0 exposes the renderer on all network interfaces. Use this only behind private networking, firewall/ALB rules, or with password authentication enabled. If those protections are not in place, keep host on localhost/loopback.
  3. logLevel (default: process.env.RENDERER_LOG_LEVEL || 'info') - The renderer log level. Set it to silent to turn logging off. Available levels: { fatal: 60, error: 50, warn: 40, info: 30, debug: 20, trace: 10 }. silent can be used as well.
  4. logHttpLevel (default: process.env.RENDERER_LOG_HTTP_LEVEL || 'error') - The HTTP server log level (same allowed values as logLevel).
  5. fastifyServerOptions (default: {}) - Additional options to pass to the Fastify server factory. See Fastify documentation.
  6. serverBundleCachePath (default: process.env.RENDERER_SERVER_BUNDLE_CACHE_PATH || process.env.RENDERER_BUNDLE_PATH || '/tmp/react-on-rails-pro-node-renderer-bundles' ) - Path to a cache directory where uploaded server bundle files will be stored. This is distinct from Shakapacker's public asset directory. For example you can set it to path.resolve(__dirname, './.node-renderer-bundles') if you configured renderer from the / directory of your app.
  7. workersCount (default: process.env.RENDERER_WORKERS_COUNT || defaultWorkersCount() where default is your CPUs count - 1) - Number of workers that will be forked to serve rendering requests. If you set this manually make sure that value is a Number and is >= 0. Setting this to 0 will run the renderer in a single process mode without forking any workers, which is useful for debugging purposes. For production use, the value should be >= 1.
  8. password (default: env.RENDERER_PASSWORD) - The password expected to receive from the Rails client to authenticate rendering requests. In development and test environments (checked via both NODE_ENV and RAILS_ENV), the password is optional — if unset, no authentication is required. In all other environments (staging, production, etc.), the renderer will refuse to start without an explicit password. Set RENDERER_PASSWORD in your environment or pass password in the config object.
  9. allWorkersRestartInterval (default: env.RENDERER_ALL_WORKERS_RESTART_INTERVAL) - Interval in minutes between scheduled restarts of all workers. By default restarts are not enabled. If restarts are enabled, delayBetweenIndividualWorkerRestarts should also be set. Recommended for production — rolling restarts are the primary safety net against memory leaks from application code. See the Memory Leaks guide.
  10. delayBetweenIndividualWorkerRestarts (default: env.RENDERER_DELAY_BETWEEN_INDIVIDUAL_WORKER_RESTARTS) - Interval in minutes between individual worker restarts (when cluster restart is triggered). By default restarts are not enabled. If restarts are enabled, allWorkersRestartInterval should also be set. Set this high enough so that not all workers are down simultaneously (e.g., if you have 4 workers and set this to 5 minutes, the full restart cycle takes 20 minutes).
  11. gracefulWorkerRestartTimeout: (default: env.GRACEFUL_WORKER_RESTART_TIMEOUT) - Time in seconds that the master waits for a worker to gracefully restart (after serving all active requests) before killing it. Use this when you want to avoid situations where a worker gets stuck in an infinite loop and never restarts. This config is only usable if worker restart is enabled. The timeout starts when the worker should restart; if it elapses without a restart, the worker is killed.
  12. maxDebugSnippetLength (default: 1000) - If the rendering request is longer than this, it will be truncated in exception and logging messages.
  13. supportModules - (default: env.RENDERER_SUPPORT_MODULES || null) - If set to true, supportModules enables the server-bundle code to call a default set of NodeJS global objects and functions that get added to the VM context: { Buffer, TextDecoder, TextEncoder, URLSearchParams, ReadableStream, process, setTimeout, setInterval, setImmediate, clearTimeout, clearInterval, clearImmediate, queueMicrotask }. This option is required to equal true if you want to use loadable components. Setting this value to false causes the NodeRenderer to behave like ExecJS. See also stubTimers.
  14. additionalContext - (default: null) - additionalContext enables you to specify additional NodeJS objects (usually from https://nodejs.org/api/globals.html) to add to the VM context in addition to our supportModules defaults. Object shorthand notation may be used, but is not required. Example: { URL, Crypto }
  15. stubTimers - (default: env.RENDERER_STUB_TIMERS if that environment variable is set, true otherwise) - With this option set, use of functions setTimeout, setInterval, setImmediate, clearTimeout, clearInterval, clearImmediate, and queueMicrotask will do nothing during server-rendering. This is useful when using dependencies like react-virtuoso that use these functions during hydration. In RORP, hydration typically is synchronous and single-task (unless you use streaming) and thus callbacks passed to task-scheduling functions should never run during server-side rendering. Because these functions are valid client-side, they are ignored on server-side rendering without errors or warnings. See also supportModules.

Deprecated options:

  1. bundlePath - Renamed to serverBundleCachePath. The old name will continue to work but will log a deprecation warning.
  2. honeybadgerApiKey, sentryDsn, sentryTracing, sentryTracesSampleRate - Deprecated and have no effect. If you have any of them set, see Error Reporting and Tracing for the new way to set up error reporting and tracing.
  3. includeTimerPolyfills - Renamed to stubTimers.

Example Launch Files

Testing example:

spec/dummy/client/node-renderer.js

Simple example:

Create a file client/node-renderer.js. The generator uses this filename and CommonJS syntax so the file runs directly with node client/node-renderer.js without extra ESM configuration.

const path = require('path');
const { reactOnRailsProNodeRenderer } = require('react-on-rails-pro-node-renderer');

const config = {
// Save bundles to relative "./.node-renderer-bundles" dir of our app root
serverBundleCachePath: path.resolve(__dirname, '../.node-renderer-bundles'),

// All other values are the defaults, as described above
};

// For debugging, run in single process mode
if (process.env.NODE_ENV === 'development') {
config.workersCount = 0;
}
// Renderer detects a total number of CPUs on virtual hostings like Heroku or CircleCI instead
// of CPUs number allocated for current container. This results in spawning many workers while
// only 1-2 of them really needed.
else if (process.env.CI) {
config.workersCount = 2;
}

reactOnRailsProNodeRenderer(config);

And add a root-level script to the scripts section of your package.json

  "scripts": {
"node-renderer": "node client/node-renderer.js"
},

Run the renderer with pnpm run node-renderer (or the equivalent npm/yarn command for your app).

Custom Fastify Configuration

For advanced use cases, you can customize the Fastify server instance by importing the master and worker modules directly. This is useful for:

  • Adding custom routes (e.g., /health for container health checks)
  • Registering Fastify plugins
  • Adding custom hooks for logging or monitoring

Adding a Health Check Endpoint

When running the node-renderer in Docker or Kubernetes, you may need a /health endpoint for container health checks:

The advanced examples below use ES modules for readability. If you want this file to keep running as node client/node-renderer.js, either keep using the CommonJS pattern shown in the simple example above or switch the file to .mjs or "type": "module".

import masterRun from 'react-on-rails-pro-node-renderer/master';
import run, { configureFastify } from 'react-on-rails-pro-node-renderer/worker';
import cluster from 'cluster';

const config = {
// Your configuration options here
};

// Register a custom health check route
configureFastify((app) => {
app.get('/health', (request, reply) => {
reply.send({ status: 'ok' });
});
});

// The node-renderer uses Node.js cluster module to fork worker processes.
// The primary process manages workers; workers handle HTTP requests.
if (cluster.isPrimary) {
masterRun(config);
} else {
run(config);
}

Registering Fastify Plugins

You can also register Fastify plugins. This example assumes you're using the same cluster setup pattern shown above:

// In the worker branch of your cluster setup (see example above)
import run, { configureFastify } from 'react-on-rails-pro-node-renderer/worker';
import cors from '@fastify/cors';

configureFastify((app) => {
// Register a plugin
app.register(cors, {
origin: true,
});
});

// Add request logging
configureFastify((app) => {
app.addHook('onRequest', (request, reply, done) => {
try {
console.log(`Request: ${request.method} ${request.url}`);
done();
} catch (err) {
done(err);
}
});
});

Note: The configureFastify function must be called before calling run(). Multiple callbacks can be registered and will execute in order. You can use app.ready() in your callback to ensure all plugins are loaded before performing operations that depend on them.

API Stability

The ./master and ./worker exports provide direct access to the node-renderer internals. While we strive to maintain backwards compatibility, these are considered advanced APIs. If you only need basic configuration, prefer using the standard reactOnRailsProNodeRenderer function with the configuration options documented above.