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

parax

v3.0.0

Published

Run and keep alive multiple processes in parallel from a JSON descriptor

Readme

parax

parallel exec

parax runs multiple commands in parallel from a JSON descriptor file. Each command's output goes to its own log file, while parax shows a status dashboard. It supports restarting crashed processes and a built-in web server that tracks restart counts.

Installation

npm install --global parax

Requires Node.js 18.20 or newer on Linux or macOS. Windows is not supported: parax relies on POSIX process groups.

Usage

parax commands.json

Descriptor

The descriptor is validated before anything starts. Unknown keys, wrong types and out-of-range values are rejected with a one-line error such as parax: commands.json: unknown key "keepalive" in descriptor, and parax exits with code 1. Strings must not contain null bytes.

commands (required)

Array of command objects to run in parallel.

| Field | Type | Required | Description | | ------ | -------- | -------- | -------------------------------------------------------------------- | | cmd | string | yes | The executable to run (not empty, not run through a shell) | | log | string | yes | Log file for the process's stdout and stderr (appended, see below) | | name | string | no | Name shown in the status output (default: cmd and args joined) | | args | string[] | no | Arguments to pass to the executable (default: []) | | cwd | string | no | Working directory for the process | | env | object | no | Environment variables for the process (replaces parent env) |

Log files

Each process's stdout and stderr are appended to its log file, which is created with mode 0640 (before umask) if missing. Relative paths are resolved against parax's working directory, not the command's cwd. The file is reopened on every restart, so rotating it by renaming takes effect on the next restart; copytruncate works at any time. For security, parax refuses to write through a symlink at the log path (ELOOP). A log file that cannot be opened counts as a failed start. Processes get no stdin.

Status output

When stdout is a terminal, parax shows a dashboard that is redrawn whenever a process starts or exits:

COMMAND   STATUS      RESTARTS  STARTED              LAST EXIT   LOG
api       running     0         2025-01-15 09:30:00  -           /var/log/api.log
worker    restarting  7         2025-01-15 09:32:10  code 1      /var/log/worker.log
  └ Error: listen EADDRINUSE: address already in use :::3000

After a failed run, the └ line shows the last line that run wrote to its log, so startup crashes are visible without opening the log files. Terminal escape sequences are removed from it.

When stdout is not a terminal (systemd, docker, redirected to a file), parax prints the table at startup and every 5 minutes, plus one timestamped line whenever a process exits:

2025-01-15 09:32:10 worker exited with code 1. Restarting... └ Error: listen EADDRINUSE: address already in use :::3000

Descriptor options

keepAlive

Boolean. When true, any process that exits is automatically restarted. Defaults to false.

loopMillis

Integer from 0 to 2147483647. Delay (in milliseconds) before restarting an exited process when keepAlive is true. Defaults to 2000.

webPort

Integer from 1 to 65535. When set, starts an HTTP server on 127.0.0.1 at the given port exposing restart counts at GET /restarts. It is only reachable from the local machine.

Requests must use 127.0.0.1:<port> or localhost:<port> as the Host header; anything else gets 403. This blocks websites from reading the endpoint through DNS rebinding. The server accepts at most 32 connections and closes a connection that has not sent a complete request within 2 seconds.

Exit code

  • 0 when all commands exited with code 0, or when parax was stopped by a signal.
  • 1 when any command exited with a non-zero code, was killed by a signal, or could not be started (without keepAlive), or when the descriptor is invalid.

With keepAlive, parax only exits when stopped by a signal.

Signals

Each command runs in its own process group. When a command's main process exits, any processes it left behind (e.g. under sh -c or npm run) receive SIGTERM. With keepAlive, whatever is still left of that group receives SIGKILL right before the restart, so leftovers get loopMillis to shut down gracefully.

On SIGINT, SIGTERM or SIGHUP, parax stops restarting processes, forwards the signal to every command's process group, and exits once all main processes have exited. A second signal while stopping sends SIGKILL to every process group instead.

When running parax as PID 1 in a container, use docker run --init (or tini). Parax cannot reap orphaned grandchildren, so without an init process they remain as zombies.

Security

  • The descriptor is code: whoever can edit it can run any command as the user running parax. Make sure only that user can write to it, and never run parax as root with a descriptor other users can modify.
  • Commands run without a shell, so values in cmd, args, cwd and env are never interpreted as shell syntax.
  • Commands inherit parax's user and privileges. Run parax as an unprivileged user where possible.
  • Log files are opened by parax with its privileges. Keep log directories writable only by trusted users; parax does not follow a symlink at the log path itself, but does follow symlinked directories.
  • /restarts shows full command lines, including any secrets passed as arguments. It is only reachable from the local machine (see webPort).

Examples

Basic parallel execution:

{
    "commands": [
        { "name": "frontend", "cmd": "npm", "args": ["run", "dev"], "cwd": "./frontend", "log": "frontend.log" },
        { "name": "backend", "cmd": "npm", "args": ["run", "start"], "cwd": "./backend", "log": "backend.log" }
    ]
}

Keep services alive:

{
    "keepAlive": true,
    "commands": [
        { "cmd": "node", "args": ["db.js"], "log": "/var/log/db.log" },
        { "cmd": "node", "args": ["server.js"], "log": "/var/log/server.log" }
    ]
}

Track restarts via the web server:

{
    "keepAlive": true,
    "webPort": 3000,
    "commands": [
        { "cmd": "node", "args": ["worker.js"], "log": "worker.log" },
        { "cmd": "node", "args": ["api.js"], "log": "api.log" }
    ]
}
GET http://localhost:3000/restarts

[
  { "cmd": "node worker.js", "restarts": 2, "uptimeMs": 12000 },
  { "cmd": "node api.js", "restarts": 0, "uptimeMs": 60000 }
]

uptimeMs is the time in milliseconds since the current process instance started (reset on each restart). It is null while the process is not running.

Development

pnpm install
pnpm check   # type-check source and tests
pnpm test    # build, then run the test suite

The test suite uses Node's built-in test runner and requires Node 22.18 or newer (TypeScript type stripping) on a POSIX system. Tests run the built dist/index.js as a black box. To run parax itself under a different Node.js version than the test runner, set PARAX_NODE to that node binary:

PARAX_NODE=/path/to/node-18/bin/node pnpm test

npm pack and npm publish run the type check and the full test suite first (prepack).