{"id":13680493,"url":"https://github.com/godaddy/node-cluster-service","last_synced_at":"2025-07-27T10:21:13.575Z","repository":{"id":9983238,"uuid":"12011831","full_name":"godaddy/node-cluster-service","owner":"godaddy","description":"Turn your single process code into a fault-resilient, multi-process service with built-in REST \u0026 CLI support. Restart or hot upgrade your web servers with zero downtime or impact to clients.","archived":false,"fork":false,"pushed_at":"2023-01-06T14:41:54.000Z","size":865,"stargazers_count":171,"open_issues_count":14,"forks_count":28,"subscribers_count":29,"default_branch":"master","last_synced_at":"2025-04-17T13:37:08.965Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.npmjs.org/package/cluster-service","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/godaddy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-08-09T22:30:28.000Z","updated_at":"2025-02-10T21:40:58.000Z","dependencies_parsed_at":"2023-01-13T15:40:30.734Z","dependency_job_id":null,"html_url":"https://github.com/godaddy/node-cluster-service","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Fnode-cluster-service","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Fnode-cluster-service/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Fnode-cluster-service/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Fnode-cluster-service/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/godaddy","download_url":"https://codeload.github.com/godaddy/node-cluster-service/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251600373,"owners_count":21615696,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T13:01:17.687Z","updated_at":"2025-04-29T23:31:50.635Z","avatar_url":"https://github.com/godaddy.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# cluster-service\n\n[![Build Status](https://travis-ci.org/godaddy/node-cluster-service.png)](https://travis-ci.org/godaddy/node-cluster-service) [![NPM version](https://badge.fury.io/js/cluster-service.png)](http://badge.fury.io/js/cluster-service) [![Dependency Status](https://gemnasium.com/godaddy/node-cluster-service.png)](https://gemnasium.com/godaddy/node-cluster-service) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/godaddy/node-cluster-service/trend.png)](https://bitdeli.com/free \"Bitdeli Badge\")\n\n[![NPM](https://nodei.co/npm/cluster-service.png?downloads=true\u0026stars=true\u0026downloadRank=true)](https://www.npmjs.org/package/cluster-service) [![NPM](https://nodei.co/npm-dl/cluster-service.png?height=2)](https://nodei.co/npm/cluster-service/)\n\n## Install\n\n\tnpm install cluster-service\n\nhttps://npmjs.org/package/cluster-service\n\n\n\n## About\n\n\tTurn your single process code into a fault-resilient, multi-process service with\n\tbuilt-in REST \u0026 CLI support. Restart or hot upgrade your web servers with zero\n\tdowntime or impact to clients.\n\nPresentation:\n\nhttp://x.co/bpnode\n\nVideo:\n\nhttp://x.co/bpnodevid\n\n\n## Getting Started\n\nYour existing application, be it console app or service of some kind:\n\n\t// server.js\n\tconsole.log(\"Hello World\");\n\nLeveraging ```cluster-service``` without adding a line of code:\n\n\tnpm install -g cluster-service\n\tcservice \"server.js\" --accessKey \"lksjdf982734\"\n\t// cserviced \"server.js\" --accessKey \"lksjdf982734\" // daemon\n\nThis can be done without a global install as well, by updating your ```package.json```:\n\n\t\"scripts\": {\n\t\t\"start\": \"cservice server.js --accessKey lksjdf982734\"\n\t},\n\t\"dependencies\": {\n\t\t\"cluster-service\": \"\u003e=0.5.0\"\n\t}\n\nNow we can leverage ```npm``` to find our local install of ```cluster-service```:\n\n\tnpm start\n\nOr, if you prefer to control ```cluster-service``` within your code, we've got you covered:\n\n\t// server.js\n\trequire(\"cluster-service\").start({ workers: \"./worker.js\", accessKey: \"lksjdf982734\" });\n\n\t// worker.js\n\tconsole.log(\"Hello World\"); // notice we moved our original app logic to the worker\n\n\n\n## Talk to it\n\nNow that your service is resilient to worker failure, and utilizing all cores of your machine, lets talk to it.\nWith your service running, type into the command-line:\n\n\trestart all\n\nor for a full list of commands...\n\n\thelp\n\nor for help on a specific command:\n\n\thelp {command}\n\nWe can also issue commands from a seperate process, or even a remote machine (assuming proper access):\n\n\tnpm install -g cluster-service\n\tcservice \"restart all\" --accessKey \"my_access_key\"\n\nYou can even pipe raw JSON for processing:\n\n\tcservice \"restart all\" --accessKey \"my_access_key\" --json\n\nCheck out ***Cluster Commands*** for more.\n\n\n\n## Start Options\n\nWhen initializing your service, you have a number of options available:\n\n\tcservice \"server.js\" --accessKey \"123\"\n\nOr via JSON config:\n\n\tcservice \"config.json\"\n\nOr within your node app:\n\n\t// server.js\n\t// inline options\n\trequire(\"cluster-service\").start({ workers: \"worker.js\", accessKey: \"123\" });\n\t// or via config\n\trequire(\"cluster-service\").start({ config: \"config.json\" });\n\n### Options:\n\n* `workers` - Path of worker to start. A string indicates a single worker,\n  forked based on value of ```workerCount```. An object indicates one or more worker objects:\n  ```{ \"worker1\": { worker: \"worker1.js\", cwd: process.cwd(), count: 2, restart: true } }```.\n  This option is automatically set if run via command-line ```cservice \"worker.js\"``` if\n  the ```.js``` extension is detected.\n* `accessKey` - A secret key that must be specified if you wish to invoke commands from outside\n  your process. Allows CLI \u0026 REST interfaces.\n* `config` - A filename to the configuration to load. Useful to keep options from having to be inline.\n  This option is automatically set if run via command-line ```cservice \"config.json\"``` if\n  the ```.json``` extension is detected.\n* `host` (default: \"localhost\") - Host to bind to for REST interface. (Will only bind if `accessKey`\n  is provided)\n* `port` (default: 11987) - Port to bind to. If you leverage more than one cluster-service on a\n  machine, you'll want to assign unique ports. (Will only bind if accessKey is provided)\n* `workerCount` (default: os.cpus().length) - Gives you control over the number of processes to\n  run the same worker concurrently. Recommended to be 2 or more to improve availability. But some\n  workers do not impact availability, such as task queues, and can be run as a single instance.\n* `cli` (default: true) - Enable the command line interface. Can be disabled for background\n  services, or test cases. Running `cserviced` will automatically disable the CLI.\n* `ssl` - If provided, will bind using HTTPS by passing this object as the\n  [TLS options](http://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener).\n* `run` - Ability to run a command, output result, and exit. This option is automatically\n  set if run via command-line ```cservice \"restart all\"``` and no extension is detected.\n* `json` (default: false) - If specified in conjunction with ```run```,\n  will *only* output the result in JSON for\n  consumption from other tasks/services. No other data will be output.\n* `silent` (default: false) - If true, forked workers will not send their output to parent's stdio.\n* `allowHttpGet` (default: false) - For development purposes, can be enabled for testing, but is\n  not recommended otherwise.\n* `restartOnMemUsage` (default: disabled) - If a worker process exceeds the specified memory threshold\n  (in bytes), the process will be restarted gracefully. Only one worker will be restarted at a time.\n* `restartOnUpTime` (default: disabled) - If a worker process exceeds the specified uptime threshold\n  (in seconds), the process will be restarted gracefully. Only one worker will be restarted at a time.\n* `restartConcurrencyRatio` (default `0.33`) - The ratio of workers that can be restarted concurrently\n  during a restart or upgrade process. This can greatly improve the speed of restarts for applications\n  with many concurrent workers and/or slow initializing workers.\n* `commands` - A single directory, an array of directories, or a comma-delimited list of directories\n  may be provided to auto-register commands found in the provided folders that match the \".js\"\n  extension. If the module exposes the \"id\" property, that will be the name of the command,\n  otherwise the filename (minus the extension) will be used as the name of the command. If relative\n  paths are provided, they will be resolved from process.cwd().\n* `master` - An optional module to execute for the master process only, once ```start``` has been completed.\n* `proxy` - Optional path to a JSON config file to enable Proxy Support.\n* `workerGid` - Group ID to assign to child worker processes (recommended `nobody`).\n* `workerUid` - User ID to assign to child worker processes (recommended `nobody`).\n\n\n## Console \u0026 REST API\n\nA DPS Cluster Service has two interfaces, the console (stdio), and an HTTP REST API. The two\ninterfaces are treated identical, as console input/output is piped over the REST API. The\nreason for the piping is that a DPS Cluster Service is intentionally designed to only\nsupport one instance of the given service running at any one time, and the port binding\nis the resource constraint. This allows secondary services to act as console-only\ninterfaces as they pipe all input/output over HTTP to the already running service\nthat owns the port. This flow enables the CLI to background processes.\nThe REST API is locked to a \"accessKey\" expected in the query string. The console\nautomatically passes this key to the REST API, but for external REST API access,\nthe key will need to be known.\n\n\t{ host: \"localhost\", port: 11987, accessKey: \"lksjdf982734\" }\n\nInvoking the REST interface directly would look something like:\n\n\tcurl -d \"\" \"http://localhost:11987/cli?cmd=help\u0026accessKey=lksjdf982734\"\n\nOr better yet, use the ```run``` option to do the work for you:\n\n\tcservice \"help\" --accessKey \"lksjdf982734\"\n\t// same as\n\tcservice --run \"help\" --accessKey \"lksjdf982734\"\n\n\n\n## Cluster Commands\n\nWhile a Cluster Service may provide its own custom commands, below are provided out-of-the-box.\nCommands may be disabled by overriding them.\n\n* `start workerPath [cwd] { [timeout:60000] }` - Gracefully start service, one worker at a time.\n* `restart all|pid { [timeout:60000] }` - Gracefully restart service, waiting up to timeout before terminating workers.\n* `shutdown all|pid { [timeout:60000] }` - Gracefully shutdown service, waiting up to timeout before terminating workers.\n* `exit now` - Forcefully exits the service.\n* `help [cmd]` - Get help.\n* `upgrade all|pid workerPath { [cwd] [timeout:60000] }` - Gracefully upgrade service, one worker at a time. (continuous deployment support).\n* `workers` - Returns list of active worker processes.\n* `health` - Returns health of service. Can be overidden by service to expose app-specific data.\n* `info` - Returns summary of process \u0026 workers.\n\n\n\n## Commands \u0026 Events\n\nCreating custom, or overriding commands and events is as simple as:\n\n  cservice \"server.js\" --commands \"./commands,../some_more_commands\"\n\nOr if you prefer to manually do so via code:\n\n\tvar cservice = require(\"cluster-service\");\n\tcservice.on(\"custom\", function(evt, cb, arg1, arg2) { // \"custom\" command\n\t\t// can also fire custom events\n\t\tcservice.trigger(\"on.custom.complete\", 1, 2, 3);\n\t};\n\n\tcservice.on(\"test\", function(evt, cb, testScript, timeout) { // we're overriding the \"test\" command\n\t\t// arguments\n\t\t// do something, no callback required (events may optionally be triggered)\n\t};\n\n\t// can also issue commands programatically\n\tcservice.trigger(\"custom\", function(err) { /* my callback */ }, \"arg1value\", \"arg2value\");\n\n\n## Cluster Events\n\nEvents are emitted to interested parties.\n\n* `workerStart (pid, reason)` - Upon exit of any worker process, the process id of the exited worker. Reasons include: \"start\", \"restart\", \"failure\", and \"upgrade\".\n* `workerExit (pid, reason)` - Upon start of any worker process. Reasons include: \"start\", \"restart\", \"failure\", and \"upgrade\".\n\n\n\n## Async Support\n\nWhile web servers are automatically wired up and do not require async logic (as of v1.0), if\nyour service requires any other asynchronous initialization code before being ready, this\nis how it can be done.\n\nHave the worker inform the master once it is actually ready:\n\n\t// worker.js\n\trequire(\"cluster-service\").workerReady(false); // we're NOT ready!\n\tsetTimeout(funtion() {\n\t\t// dumb example of async support\n\t\trequire(\"cluster-service\").workerReady(); // we're ready!\n\t}, 1000);\n\nAdditionally, a worker may optionally perform cleanup tasks prior to exit, via:\n\n\t// worker.js\n\trequire(\"cluster-service\").workerReady({\n\t\tonWorkerStop: function() {\n\t\t\t// lets clean this place up\n\t\t\tprocess.exit(); // we're responsible for exiting if we register this cb\n\t\t}\n\t});\n\n\n\n## Access Control\n\nCommands may be granted \"inproc\" (high risk), \"local\" (low risk), or \"remote\" (no risk). Setting\naccess control can be done within the command, like so:\n\n```javascript\n// exit.js\nmodule.exports.control = function(){\n\treturn \"local\";\n};\n```\n\nOr may be overriden at runtime via:\n\n```javascript\n// server.js\nrequire(\"cluster-service\").control({ \"exit\": \"local\" });\n```\n\n## Proxy Support\n\nProxy mode specifically caters to Web Servers that you want to enable automatic\nversioning of your service. Any version requested (via `versionHeader`) that is\nnot yet loaded will automatically have a worker process spun up with the new\nversion, and after ready, the proxy will route to that worker.\n\nEvery version of your app *must* adhere to the `PROXY_PORT` environment\nvariable like so:\n\n\trequire(\"http\").createServer(function(req, res) {\n\t  res.writeHead(200);\n\t  res.end(\"Hello world!\");\n\t}).listen(process.env.PROXY_PORT || 3000 /* port to use when not running in proxy mode */);\n\n### Proxy Options\n\n* `versionPath` (default: same directory as proxy JSON config) - Can override\n  to point to a new version folder.\n* `defaultVersion` - The version (folder name) that is currently active/live.\n  If you do not initially set this option, making a request to the Proxy without\n\ta `versionHeader` will result in a 404 (Not Found) since there is no active/live\n\tversion.\n  Upgrades will automatically update this option to the latest upgraded version.\n* `versionHeader` (default: `x-version`) - HTTP Header to use when determining\n  non-default version to route to.\n* `workerFilename` (default: `worker.js`) - Filename of worker file.\n* `bindings` (default: `[{ port: 80, workerCount: 2 }]`) - An array of `Proxy Bindings`.\n* `versionPorts` (default: `11000-12000`) - Reserved port range that can be used to\n  assign ports to different App versions via `PROXY_PORT`.\n* `nonDefaultWorkerCount` (default: 1) - If a version is requested that is not\n  a default version, this will be the number of worker processes dedicated to\n\tthat version.\n* `nonDefaultWorkerIdleTime` (default: 3600) - The number of seconds of inactivity\n  before a non-default version will have its workers shut down.\n\n### Proxy Bindings\n\nBinding options:\n\n* `port` - Proxy port to bind to.\n* `workerCount` (default: 2) - Number of worker processes to use for this\n  binding. Typically more than 2 is unnecessary for a proxy, and less than 2\n\tis a potential failure point if a proxy worker ever goes down.\n* `tlsOptions` - TLS Options if binding for HTTPS.\n  * `key` - Filename that contains the Certificate Key.\n\t* `cert` - Filename that contains the Certificate.\n\t* `pem` - Filename that contains the Certificate PEM if applicable.\n\nA full list of TLS Options: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener\n\n### Proxy Commands\n\nWork like any other `Cluster Commands`.\n\n* `proxy start configPath` - Start the proxy using the provided JSON config file.\n* `proxy stop` - Shutdown the proxy service.\n* `proxy version workerVersion workerCount` - Set a given App version to the\n  desired number of worker processes. If the version is not already running,\n\tit will be started. If 2 workers for the version are already running, and you\n\trequest 4, 2 more will be started. If 4 workers for the version are already\n\trunning, and you request 2, 2 will be stopped.\n* `proxy promote workerVersion workerCount` - Works identical to the\n  `proxy version` command, except this will flag the version as active/live,\n\tresulting in the Proxy Config file being updated with the new `defaultVersion`.\n* `proxy info` - Fetch information about the proxy service.\n\n\n\n## Tests \u0026 Code Coverage\n\nDownload and install:\n\n\tgit clone https://github.com/godaddy/node-cluster-service.git\n\tcd node-cluster-service\n\tnpm install\n\nNow test:\n\n\tnpm test\n\nView code coverage in any browser:\n\n\tcoverage/lcov-report/index.html\n\n\n## Change Log\n\n[Change Log](https://github.com/godaddy/node-cluster-service/blob/master/CHANGELOG.md)\n\n\n\n## License\n\n[MIT](https://github.com/godaddy/node-cluster-service/blob/master/LICENSE.txt)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgodaddy%2Fnode-cluster-service","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgodaddy%2Fnode-cluster-service","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgodaddy%2Fnode-cluster-service/lists"}