The daily ritual for anyone running more than one process in local dev: a
terminal tab for the web server, another for the worker, another for the CSS
watcher, maybe a fourth for the queue. Now you're alt-tabbing between tabs trying
to remember which one had the error — and when you're done you kill them one… by…
one (and miss the orphaned tailwind --watch still pinning a CPU core an hour
later).
The fix has existed for over a decade: a Procfile. List your processes once,
run them all together, get their output interleaved into a single stream:
web: node server.js
worker: node worker.js
css: npx tailwindcss -i app.css -o public/app.css --watch
The problem isn't the idea — it's the tooling around it.
Why the existing runners didn't fit
- foreman is the original and it's great — but it's a Ruby gem. If your project is Node or Python, you're installing and maintaining a whole Ruby toolchain solely to babysit your dev processes.
- overmind is fast and lovely, but it drives everything through tmux — another binary to install, and your output now lives inside a tmux session instead of just scrolling past your terminal.
-
concurrentlyis genuinely excellent and mature — I'll say that plainly. But it's an npm dependency, and you spell your processes out as command-line args rather than in a checked-inProcfile.
I wanted the foreman experience — a real Procfile, prefixed output, one clean
Ctrl-C — with nothing to install but the tool itself, on whichever runtime
already happens to be on the machine. So I built proctide.
What it does
npx proctide
web | listening on http://localhost:3000
worker | polling jobs queue…
css | rebuilt app.css in 42ms
web | GET / 200 11ms
worker | processed job #1841
^C
proctide: SIGINT received, shutting down…
web | exited (signal SIGTERM)
worker | exited (signal SIGTERM)
css | exited (signal SIGTERM)
Every process starts at once. Each line of output — stdout and stderr both —
gets tagged with the process name, colored so you can pattern-match at a glance,
and padded so the | separators line up. One Ctrl-C sends SIGTERM to every
process group (so the shells and their grandchildren go down — no orphaned
sleep/watch), waits briefly, then exits. If a process crashes on its own, the
rest are torn down too and proctide exits non-zero — so it behaves in CI and
make targets, not just interactively.
It's the same tool on Node and Python
This is the part I'm a little proud of. proctide ships on both registries:
npx proctide # Node — zero deps
pipx run proctide # Python — pure stdlib
Whichever is already on the box, you can run proctide with no extra install. And
it's not two lookalike tools — the parsing-and-formatting core (parseProcfile,
colorFor, prefixLine) is the same set of pure functions in both languages,
driven by one shared input→output vector table. Both test suites run that
identical table, so the two ports are proven to format byte-for-byte the same — I
literally diff their output to prove it.
A few design notes
- A pure core, an IO shell. All the deterministic work — splitting the Procfile, picking colors, building the padded prefix — lives in pure functions with no spawn, no clock, no signals. That's what makes the cross-language guarantee possible. The messy part (concurrent spawn, interleaved streams, signal forwarding) is isolated in a separate runner module and covered by an integration smoke test, since live interleaved output is inherently nondeterministic and not worth pretending otherwise.
-
Process groups, not just processes. Each child is spawned in its own
session/group (
detachedin Node,start_new_sessionin Python), so aweb: sh -c '… && node server.js'shuts down completely instead of leaking thenode. A child that ignoresSIGTERMgetsSIGKILLas a backstop. The Python port also writes through a shared lock so two processes printing at once never produce a garbled half-and-half line.
And the honest caveat: concurrently is mature and great; if you're already in an
npm-only project it's a fine answer. proctide's pitch is narrow — a real
Procfile, prefixed output, clean process-group shutdown, zero deps on either Node
or Python. If that combination is what you've been missing, this is for you.
Try it / break it
npx proctide # or: pipx run proctide
It's MIT and tiny. I'd love to hear which shutdown edge case I haven't thought of
yet — and what are you using today to run your local dev stack: a Procfile runner,
a Makefile with &, a wall of terminal tabs, or something cleverer?
Top comments (0)