local dev process manager
  • Rust 89.3%
  • HTML 8.9%
  • Procfile 1.8%
Find a file
2026-08-18 17:47:47 -06:00
src v0.3.11: fix PID tracking and duplicate processes — check running state before spawning to avoid leaking duplicates; kill orphaned process groups on daemon restart instead of fake re-attach; idempotent sync that verifies the recorded PID is alive; restore full process defs and persist state on crash/restart; fix start <name>; interpolate ${VAR} in .env and apply configured env on spawn 2026-08-18 17:47:32 -06:00
static v0.3.6: make links more prominent in web UI 2026-07-31 15:50:01 -06:00
test_fixtures Sync updates metadata (links, lifecycle, backoff) on existing processes 2026-07-31 14:07:33 -06:00
.clippy.toml TOML procfile support: lifecycle, backoff, env_file, cwd, links 2026-07-31 13:59:16 -06:00
.gitignore Initial commit: backfin process manager 2026-07-31 10:47:03 -06:00
Cargo.lock update Cargo.lock for 0.3.11 2026-08-18 17:47:47 -06:00
Cargo.toml bump version to 0.3.11 for publish 2026-08-18 17:47:41 -06:00
LICENSE AGPL-3.0 license 2026-07-31 10:50:33 -06:00
README.md Show links in CLI status output and update README with TOML docs 2026-07-31 14:00:20 -06:00
rustfmt.toml TOML procfile support: lifecycle, backoff, env_file, cwd, links 2026-07-31 13:59:16 -06:00

backfin

local dev process manager. reads a Procfile, runs everything concurrently.

cargo install backfin

why

single Rust binary — daemon, CLI client, and web dashboard all in one.

quick start

cat > Procfile << 'EOF'
web: echo "starting web" && while true; do echo "request GET /"; sleep 3; done
worker: echo "starting worker" && while true; do echo "job done"; sleep 5; done
EOF

backfin start

commands

command what it does
backfin start start daemon + all Procfile processes, print status, exit
backfin start <name> start a specific process
backfin status table: name, status, pid, uptime, restart count
backfin stop <name> stop a process (resolves short names like web → project/web)
backfin stop-all stop every managed process
backfin restart <name> restart a process
backfin logs <name> tail logs for a process via socket
backfin web start the web dashboard on port 9999

procfile formats

backfin supports two formats. Procfile.toml is the preferred format — it's more descriptive and supports per-process configuration.

# Procfile.toml
[[processes]]
name = "web"
cmd = "cargo run"
cwd = "./web"
env = { RUST_LOG = "debug", PORT = "3000" }
links = ["http://localhost:3000"]
lifecycle = "always"      # always | on-failure | never
backoff = true            # exponential restart delay

[[processes]]
name = "worker"
cmd = "cargo run --bin worker"
env_file = ".env.worker"  # load env vars from a file
lifecycle = "on-failure"  # only restart on non-zero exit

[[processes]]
name = "oneoff"
cmd = "./scripts/migrate"
lifecycle = "never"       # never restart
backoff = false

Fields:

  • name — process name (required)
  • cmd — shell command to run (required)
  • cwd — working directory (optional, defaults to project root)
  • env — environment variables override
  • env_file — load environment from a file
  • links — URLs shown in the web dashboard
  • lifecycle — restart behavior: always (default), on-failure, never
  • backoff — use exponential backoff on restart (default: true)

Classic Procfile

# Procfile or Procfile.dev
web: cargo run
worker: cargo run --bin worker

Inline env vars are supported:

api: PORT=8080 DEBUG=true go run .

File priority: Procfile.toml > Procfile.dev > Procfile > procfile

web dashboard

backfin web        # → http://localhost:9999

built-in SPA with:

  • process table (status, pid, uptime, restarts)
  • live streaming logs over SSE
  • per-process start / stop / restart / clear buttons
  • no external dependencies — single HTML file embedded in the binary

how it works

backfin start
  ├── spawns daemon process (background)
  ├── daemon listens on ~/.backfin/daemon.sock for CLI commands
  ├── daemon serves web dashboard on port 9999
  ├── CLI parses Procfile, sends process map to daemon via socket
  ├── daemon spawns each process, reads stdout/stderr line-by-line
  ├── output is ring-buffered (last 1000 lines per process)
  ├── broadcast to subscribers (web SSE, CLI logs)
  └── CLI prints status table + web URL and exits

crashed processes restart automatically with exponential backoff: 1s → 2s → 4s → … → 60s max.

process definitions and PIDs survive daemon restarts — state is persisted to ~/.backfin/state.json. if the daemon dies, the next start detects surviving PIDs and re-attaches to them.

architecture

src/
  main.rs       CLI parsing + socket client
  procfile.rs   Procfile + .env parser
  manager.rs    ProcessManager (spawn, stop, restart, ring buffers, broadcast)
  daemon.rs     daemon (process I/O, exit monitor w/ backoff, Unix socket, PID reattach)
  web.rs        axum HTTP server (SSE, REST API, static HTML)

static/
  index.html    web dashboard (vanilla JS, no frameworks)
  • tokio async runtime
  • axum for the web server
  • owo-colors for terminal output
  • Unix socket IPC between CLI client and daemon
  • broadcast channels for log distribution
  • single binary — backfin is the daemon, the CLI, and the web server

license

AGPL-3.0