cmd / procman
procman is a process manager for local development on macOS.
It is open source.
Install:
go install github.com/croaky/procman@latest
Define processes in Procfile.dev. For example:
clock: bundle exec ruby cmd/clock.rb
esbuild: ./bin/build-js --watch
queues: bundle exec ruby cmd/queues.rb
web: bundle exec ruby cmd/web.rb
Run processes by name, comma-delimited:
procman esbuild,web
There are no options or flags.
procman combines the output:
esbuild | bundling js/app.ts, css/app.css
esbuild | wrote public/js/app-JBTSFCY2.js
esbuild | wrote public/css/app-2H67SL6V.css
esbuild | watching...
web | [67296] Puma starting in cluster mode...
web | [67296] * Puma version: 6.4.2 (ruby 3.3.0-p0) ("The Eagle of Durango")
web | [67296] * Min threads: 12
web | [67296] * Max threads: 12
web | [67296] * Environment: development
web | [67296] * Master PID: 67296
web | [67296] * Workers: 2
web | [67296] * Restarts: (✔) hot (✖) phased
web | [67296] * Preloading application
web | [67296] * Listening on http://0.0.0.0:3000
web | [67296] Use Ctrl-C to stop
web | [67296] - Worker 1 (PID: 67331) booted in 0.9s, phase: 0
web | [67296] - Worker 0 (PID: 67330) booted in 0.9s, phase: 0
procman runs until it receives SIGINT (Ctrl+C),
SIGTERM, or SIGHUP.
When one process exits, procman sends SIGINT to the others,
waits 5 seconds, and then sends SIGKILL to any that remain.
procman runs one process per definition.
It does not load .env.
File watching
Add # watch: PATTERNS to restart a process when files change:
clock: bundle exec ruby cmd/clock.rb # watch: lib/**/*.rb
esbuild: ./bin/build-js --watch
queues: bundle exec ruby cmd/queues.rb # watch: lib/**/*.rb
web: bundle exec ruby cmd/web.rb # watch: lib/**/*.rb,ui/views/**/*.haml
Patterns are relative to the directory with Procfile.dev.
Globs support * (single directory) and ** (recursive).
Separate patterns with commas.
procman ignores directories matched by .gitignore (and .git), so
node_modules and build output do not consume watch descriptors.
When files change, procman sends SIGINT, waits for the process to exit,
and restarts it.
It debounces changes (100ms) so a multi-file save restarts once.
It serializes restarts per process.
Reloading at the process manager level works in any language without framework support.
Kill a process on a port
When a process outlives procman and holds its port, a script on
$PATH clears it:
kill-pid-on-port 3000
#!/bin/bash
set -euo pipefail
lsof -n -i :"$1" | grep LISTEN | awk '{ print $2 }' | xargs kill
lsof -ti :3000 | xargs kill is shorter, but -ti returns every
process on the port, clients included. grep LISTEN limits it to the
server.