npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@goopil/clusterkit

v1.1.1

Published

Zero-dependency Node.js cluster orchestrator for containers: SO_REUSEPORT detection, graceful shutdown with ACK protocol, crash circuit breaker, worker recycling and cgroup-aware sizing.

Readme

@goopil/clusterkit

Core orchestrator for multi-worker Node.js servers.

This README focuses on the package-level contract (capabilities, options, API). For monorepo context and examples catalog, see the root README.

Capabilities

| Capability | Details | |------------|---------| | Worker orchestration | Spawns and supervises workers with cluster | | Platform capability detection | Orchestrator.getCapabilities() reports platform, reusePort, clusterRecommended | | Crash protection | Exponential restart backoff + circuit breaker (restart.*) | | Graceful shutdown | ACK-based worker shutdown, configurable timeouts/signals (shutdown.*) | | Lifecycle controls | Worker recycling (workers.maxAgeMs), env patching and worker-count override APIs | | Observability | Typed events, getMetrics(), getHealth() | | Plugin system | use(plugin) with install/uninstall lifecycle |

Installation

pnpm add @goopil/clusterkit

Quick start

import { createServer } from "node:http";
import { Orchestrator } from "@goopil/clusterkit";

const orchestrator = new Orchestrator({ logger: console });

orchestrator.run(async () => {
  const capabilities = await Orchestrator.getCapabilities();

  const server = createServer((_req, res) => {
    res.end("ok");
  });

  server.listen({
    port: 3000,
    host: "0.0.0.0",
    reusePort: capabilities.reusePort,
    exclusive: capabilities.reusePort,
  });

  orchestrator.registerOnShutdown(() => server.close());
});

Configuration options

Top-level (OrchestratorConfig)

| Option | Type | Default | Description | |--------|------|---------|-------------| | logger | Logger \| null | null | pino/winston/console-compatible logger | | workers | WorkersConfig | {} | Worker count/process options | | restart | RestartConfig | {} | Crash handling + restart policy | | shutdown | ShutdownConfig | {} | Shutdown lifecycle options |

workers

| Option | Type | Default | Description | |--------|------|---------|-------------| | count | number \| 'auto' | 'auto' | Number of workers | | env | NodeJS.ProcessEnv | undefined | Extra env vars merged into worker env | | execArgv | string[] | undefined | Node.js args passed to workers | | maxAgeMs | number | 0 | Worker recycling interval (0 disables) |

restart

| Option | Type | Default | Description | |--------|------|---------|-------------| | crashThreshold | number | 5 | Crash count before tripping circuit breaker | | crashWindowMs | number | 60000 | Sliding window for crash counting | | backoffMs | number | 1000 | Initial restart delay | | maxBackoffMs | number | 30000 | Backoff upper bound | | backoffMultiplier | number | 2 | Exponential backoff multiplier | | stabilityWindowMs | number | 30000 | Crash-free time required to reset backoff (0 = immediate reset) |

Restart/backoff is cluster-level, not worker-local. Every non-graceful worker exit is recorded in the crash window and queued for replacement unless shutdown is already in progress or the circuit breaker has tripped. Replacements are started one at a time through the restart queue, using the current backoff delay. A healthy worker that was not part of the crash does not reset the delay by itself; backoff resets only after a replacement has stayed online for stabilityWindowMs without another crash. This keeps partial flapping conservative while avoiding a full-cluster stop unless crashThreshold is reached inside crashWindowMs.

shutdown

| Option | Type | Default | Description | |--------|------|---------|-------------| | timeoutMs | number | 12000 | Max graceful shutdown duration before forced kill | | ackTimeoutMs | number | 3000 | Max wait for worker ACK before disconnecting that worker | | messagePrefix | string | "__wm" | Internal IPC message prefix | | sigtermDelayMs | number | 2000 | Delay before escalating SIGTERM -> SIGINT | | sigintDelayMs | number | 1000 | Delay before escalating SIGINT -> SIGKILL |

Runtime API (high level)

const orchestrator = new Orchestrator(config);

await orchestrator.run(start);
orchestrator.use(plugin);
orchestrator.registerOnShutdown(cb);

orchestrator.overrideWorkerCount(4);
orchestrator.patchWorkerEnv({ NODE_OPTIONS: "--max-old-space-size=256" });

const metrics = orchestrator.getMetrics();
const health = orchestrator.getHealth();

const supportsReusePort = await Orchestrator.supportsReusePort();
const capabilities = await Orchestrator.getCapabilities();

Lifecycle and shutdown semantics

The primary process owns worker supervision, restart policy, plugin installation, and coordinated shutdown. Worker processes only run the application bootstrap passed to run() and any shutdown callbacks registered with registerOnShutdown().

During SIGTERM or SIGINT, the primary sends a shutdown IPC message to each worker and waits for one terminal outcome: worker ACK, worker exit, worker disconnect, or shutdown.ackTimeoutMs. It then disconnects remaining workers, waits up to shutdown.timeoutMs, and escalates hung workers through SIGTERM, SIGINT, then SIGKILL.

Register server cleanup in workers, not the primary:

orchestrator.run(async () => {
  const server = createServer(handler);
  server.listen(3000);

  orchestrator.registerOnShutdown(() => server.close());
});

Set shutdown.timeoutMs below your platform termination grace period so forced escalation can happen before the container or process supervisor kills the primary process.

Typed events (OrchestratorEvents)

| Event | When it fires | |-------|---------------| | worker:online | A worker reaches the cluster online state. | | worker:exit | A worker exits, including graceful disconnects and crashes. | | worker:crash | A worker exits non-gracefully and is recorded in the crash window. | | worker:restart | A replacement worker is forked after restart backoff. | | worker:recycle | A worker is replaced because workers.maxAgeMs is enabled and reached. | | shutdown:start | Primary shutdown coordination starts for SIGTERM or SIGINT. | | shutdown:complete | Primary shutdown coordination has finished. | | circuit-breaker:tripped | Crash count reached restart.crashThreshold inside restart.crashWindowMs. | | restart:start | A hot restart cycle begins via restartWorkers(). | | restart:complete | A hot restart cycle finishes — all targeted workers replaced. |

Capability helpers

Use Orchestrator.getCapabilities() when startup needs the full platform summary, and Orchestrator.supportsReusePort() when only SO_REUSEPORT support matters. Both helpers are asynchronous because capability detection can probe the runtime platform.

Related docs