proc

Every process in your Procfile, in one terminal, each in its own PTY.

The proc mark — four rounded bars in the colors proc gives its processes, each followed by a separator and a dimmed log line, on a dark rounded tile over a near-black ground

Yet another Procfile runner. foreman has been doing this for over a decade and does it well — I wanted one that gets out of the way when a process misbehaves, and I could write it.

One binary. Put a Procfile in a folder, run proc, and every line in it starts:

db: postgres -D ./tmp/db
web(delay:2s): bin/rails server
worker(restart, retries:5): bundle exec sidekiq
seed(optional, once, allow-failure): bin/rails db:seed

Each process gets its own PTY rather than a pipe. That one decision is most of the point: a program that checks isatty behaves the way it does in your own shell, so colors, progress bars and interactive prompts all survive being multiplexed into one stream. Names are colored by walking the HSL wheel at the golden angle, so the seventh process is as distinct from the sixth as the first is from the second.

The parentheses are per-process flags, and each one touches only the process that declares it. once leaves it down when it exits. restart always brings it back. stop takes the whole run down with it. optional keeps it out until it is asked for by name. delay:2s holds its start, retries:5 gives up after five, muted keeps it off the terminal, allow-failure means its non-zero exit doesn’t fail the run.

In a terminal it’s interactive by default. G opens a full-screen menu over the log stream: start, stop, restart, kill, per-process run mode, add a process to the running session, a live ps, and a dashboard that samples memory and CPU for each process’s whole group — the shell plus whatever it spawned — once a second and draws the history as sparklines. Every item has a direct letter.

The session outlives the process set. An empty table is still a prompt you can add to, and a process that exited stays listed so you can start it again. An explicit stop outranks the --on-exit policy: something you put down stays down.

Given piped input and nothing to run, it reads stdin instead and renders that through the same consumer, so journalctl -f | proc gets you the same colored, timestamped stream. Naming processes counts as asking for a run and wins over the pipe — which is what makes proc web work from a script or from CI, where stdin is never a terminal.

Where it stops. Unix only: PTYs and signals are the whole mechanism, and there is no Windows build. Thread counts in the dashboard are Linux-only, because sysinfo doesn’t expose them anywhere else, and per-process file descriptor counts aren’t portable at all, so that figure isn’t there.

Ctrl+C sends SIGINT and waits five seconds before SIGKILL. A second Ctrl+C doesn’t wait.

Rust, MIT. A prebuilt binary for macOS or Linux:

brew install jwo1f/tap/proc

Or from source:

cargo install --git https://github.com/JWo1F/proc