Back to Articles

mprocs: The Process Manager That Gives You Real TTYs, Not Just Log Streams

[ View on GitHub ]

mprocs: The Process Manager That Gives You Real TTYs, Not Just Log Streams

Hook

Most process managers lie to your programs about being in a terminal—they're just log aggregators with pretty colors. mprocs actually allocates a PTY for each process, which means you can run vim in one pane while your dev server runs in another.

Context

Every developer runs multiple processes during development: a web server, a test watcher, maybe a database, perhaps a build tool in watch mode. The traditional approach is opening multiple terminal tabs or tmux panes, manually starting each service, and clicking around to check outputs. Tools like concurrently improved this by running everything from one command, but they treat processes as log producers—merge the streams, add some prefixes, call it a day.

This falls apart the moment you need interaction. Want to attach a debugger? Need to run a REPL command? Have to manually restart just one service? You're back to managing terminal windows. mprocs emerged from this gap: what if a process manager treated each command as a first-class citizen with its own real terminal, not just a stdout pipe? Built in Rust for cross-platform compatibility, it provides a TUI where each process lives in an actual PTY, making them fully interactive while giving you a unified control interface.

Technical Insight

The architectural choice that defines mprocs is PTY allocation per process. When you spawn a process in most managers, they capture stdout/stderr as pipes. The child process sees isatty() return false, disabling color output, interactive prompts, and any terminal-aware behavior. mprocs uses platform-specific PTY APIs (pseudoterminals on Unix, conPTY on Windows) to give each process a real terminal session.

Here's a basic configuration in mprocs.yaml showing how processes are defined:

procs:
  server:
    cmd: "npm run dev"
    autostart: true
    autorestart: true
  
  test:
    cmd: "cargo watch -x test"
    autostart: true
  
  db:
    cmd:
      $select:
        darwin: "brew services start postgresql"
        linux: "docker-compose up postgres"
    stop: "SIGTERM"
    stop_timeout: 30

The $select operator demonstrates cross-platform configuration—same file works on different operating systems. Each process gets lifecycle controls: autostart for launch-on-init, autorestart for crash recovery, and stop signal customization (critical for graceful database shutdowns).

The PTY implementation enables genuine interaction. When you focus a process pane in mprocs, your keystrokes go directly to that PTY. This isn't simulated—you're actually writing to /dev/pts/X on Linux or the Windows conPTY handle. You can run vim, attach gdb, or use any interactive CLI tool. The process doesn't know it's being managed.

Process management happens through a Rust-based state machine tracking each process's lifecycle: Stopped, Starting, Running, Stopping. The TUI layer (built with the tui-rs crate, now ratatui) renders each process's terminal buffer in separate panes. When you quit mprocs, it walks through processes sending configured stop signals (defaulting to SIGTERM on Unix, taskkill on Windows), waits for stop_timeout, then forces termination if needed.

The remote control API adds programmability:

# Start a specific process
echo 'start test' | nc -U /tmp/mprocs.sock

# Send input to a running process
echo 'send server "rs\n"' | nc -U /tmp/mprocs.sock

This socket-based control (Unix domain socket on Unix, named pipe on Windows) means CI scripts or editor integrations can drive mprocs programmatically. You could bind a keyboard shortcut to restart your dev server without context-switching from your editor.

Logging is handled separately from display. Each process can write to its own log file with rotation:

procs:
  api:
    cmd: "./api-server"
    log_file: "logs/api-{timestamp}.log"
    log_truncate: false
    log_max_bytes: 10485760  # 10MB

The {timestamp} placeholder gets replaced at startup, and log_max_bytes triggers rotation. This separation means you get clean log files for debugging while the TUI shows live output—best of both worlds.

The shell execution model is worth understanding. By default, commands run through your shell ($SHELL on Unix, cmd.exe on Windows), enabling environment variable expansion and shell syntax. For security or performance, you can specify shell: null to execute binaries directly with argument arrays, avoiding shell injection risks when command strings come from untrusted sources.

Gotcha

The PTY advantage comes with a cost: no session persistence. Tmux and screen can detach and reattach, surviving SSH disconnections or system reboots. When mprocs exits—planned or crashed—all child processes die with it. There's no reattachment mechanism. This makes mprocs unsuitable for long-running production processes or situations where you need to disconnect from a session.

Terminal size handling can be quirky. Each PTY gets a fixed size (defaulting to the pane dimensions), and while mprocs sends SIGWINCH on pane resize, programs that do complex terminal queries may get confused. Full-screen terminal applications running inside mprocs panes sometimes render incorrectly compared to running in dedicated terminals. Think carefully before running complex TUI apps (like another instance of a text editor) within mprocs—it works, but you might hit edge cases with cursor positioning or screen refresh.

Configuration changes require restarts or remote control commands. You can't just add a new process to the TUI on-the-fly through the interface—you edit the YAML and restart or use the socket API to add processes programmatically. For workflows with many ephemeral or dynamically-determined processes, this becomes cumbersome. The tool is optimized for static, project-defined process sets, not ad-hoc command execution.

Verdict

Use if: You run 3-15 well-defined processes per project (dev servers, watchers, databases) and want standardized startup across your team with occasional need to interact with individual processes. Perfect for monorepo setups or microservice development where onboarding means mprocs and everything runs. The configuration-as-code approach and cross-platform support make it excellent for teams on mixed operating systems. Skip if: You need session persistence for long-running processes that survive disconnections, require dynamic process spawning without configuration changes, or already have deep tmux muscle memory with complex layout requirements. Also skip for production process management—use systemd, supervisord, or container orchestration instead. Mprocs shines in development, not deployment.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/pvolok-mprocs.svg)](https://starlog.is/api/badge-click/developer-tools/pvolok-mprocs)