A simple Rust-based process manager that reads a list of long-running processes from a configuration file, starts them in the background, and collects their logs.
- Supports configuration via
proc.toml(preferred) or a standardProcfile. - Streams all process logs to the console with prefixes by default (foreground/dev mode).
- Daemon mode via
startsubcommand, writingstdoutandstderrto per‑process files. - Gracefully shuts down all child processes on
Ctrl+C.
- Install Rust: If you don't have Rust installed, get it from rust-lang.org.
- Clone the repository:
git clone <repository-url> cd oxproc
- Build the project:
The executable will be located at
cargo build --release
target/release/oxproc.
oxproc looks for a configuration file in the current directory in the following order:
This is the recommended way to configure oxproc as it allows for more detailed control, such as specifying custom log file paths.
Example proc.toml:
[web]
cmd = "python -m http.server 8000"
stdout = "logs/web.out.log"
stderr = "logs/web.err.log"
[worker]
cmd = "while true; do echo 'Processing...'; sleep 2; done"
# stdout and stderr will default to worker.out.log and worker.err.logIf proc.toml is not found, oxproc will look for a standard Procfile.
Example Procfile:
web: python -m http.server 8000
worker: while true; do echo 'Processing...'; sleep 2; done
When using a Procfile, log files will be automatically named (e.g., web.out.log, web.err.log).
Run oxproc from the directory containing your configuration file (proc.toml or Procfile).
All commands accept --root <path> to point oxproc at a different project directory (where proc.toml/Procfile live). Defaults to current directory.
Examples:
./target/release/oxproc --root /path/to/project start
./target/release/oxproc --root .. status
./target/release/oxproc --root /path/to/project logs -fTo monitor the output of all processes in real time (no daemon), run:
./target/release/oxprocPress Ctrl+C to shut down children.
Start a background manager that daemonizes and writes state under $XDG_STATE_HOME/oxproc/<project-id>/:
./target/release/oxproc startWhen you start, oxproc prints where it writes state and logs, for quick diagnostics, e.g.:
Starting oxproc daemon for /path/to/project
State: /home/user/.local/state/oxproc/<project-id>
PID file: /home/user/.local/state/oxproc/<project-id>/manager.pid
Manager log: /home/user/.local/state/oxproc/<project-id>/manager.log
Follow logs immediately after starting (combined view):
./target/release/oxproc start -fCheck status of the daemonized processes (alias: ps):
./target/release/oxproc status
./target/release/oxproc psStop all processes for this project (sends SIGTERM, then SIGKILL after a grace period):
./target/release/oxproc stop --grace 5Show log file locations or follow (combined view supported):
./target/release/oxproc logs # prints tail for all processes (stdout + stderr)
./target/release/oxproc logs -f # combined tail -f for all processes
./target/release/oxproc logs -n 200 # last 200 lines (no follow)
./target/release/oxproc logs --name web -f # follow only a single process
#### Colored prefixes
When following logs or task output, oxproc prefixes each line with the process/task name in brackets. Prefixes are colorized by default when writing to a TTY.
Control color with:
- `--color auto|always|never` (CLI, highest precedence)
- `OXPROC_COLOR=auto|always|never` (env)
- `NO_COLOR` (env, disables colors)
Examples:[\u001b[34mweb\u001b[0m] server started on :3000 [\u001b[95mworker\u001b[0m] job=123 done
Note: When not a TTY (e.g., redirected to a file/CI), colors are disabled unless `--color=always` or `OXPROC_COLOR=always` is set.
### Restart
Stop then start in one command. You can add `-f` to attach to logs after restart:
```sh
./target/release/oxproc restart # stop then start
./target/release/oxproc restart --grace 5 -f # grace period and follow logs
Notes
- oxproc cleans up a stale
manager.pidautomatically if it detects the manager is not running. - State files live under
$XDG_STATE_HOME/oxproc/<project-id>/(default~/.local/state/oxproc/...).
When using proc.toml, oxproc can run one‑off tasks defined under a [tasks] table.
Example proc.toml snippet:
[web]
cmd = "npm run dev"
[tasks.build]
cmd = "cargo build"
[tasks.test]
cmd = "cargo test"Run tasks:
oxproc run frontend:build # runs [tasks.frontend.build]
oxproc frontend:build # shorthand (external subcommand)
oxproc run api:migrate -- --dry-runNotes
- Tasks are only available with
proc.toml. When using a legacyProcfile,oxproc run <task>andoxproc <task>are not supported. - Tasks execute as foreground one‑offs and inherit stdio; they do not use the daemon or log files.
- If an entry in
proc.tomldoes not have atasks.prefix, it is treated as a process (backwards compatible with existing configs). - You can still invoke with dots (e.g.,
frontend.build), but colons are preferred for CLI usage and listing.
You can define a task that triggers other tasks using run = [..]. Use parallel = true to run children concurrently.
[tasks.build]
run = ["frontend", "api"] # relative to the current namespace
# parallel = true # uncomment to run both at once
[tasks.build.frontend]
cmd = "pnpm --filter ./frontend build"
cwd = "./frontend"
[tasks.build.api]
cmd = "cargo build -p api"
Notes
- Child names are relative to the parent task’s namespace unless they contain
.or:(absolute). - Sequential groups stop on first failure; parallel groups fail if any child fails.
- Extra args are forwarded to each child (e.g.,
oxproc build -- --release). - Composite tasks cannot set
cwd(children manage their owncwd).
-
Sequential (default):
oxproc build # runs build.frontend then build.apiExample output:
▶ running build:frontend… …frontend logs… ▶ running build:api… …api logs… -
Parallel:
[tasks.build] run = ["frontend", "api"] parallel = true
oxproc build
Output is prefixed by child name, for example:
[build:frontend] … [build:api] … [build:frontend] … -
Forwarding arguments to all children:
oxproc build -- --release --verbose # becomes: "pnpm … build --release --verbose" and "cargo build -p api --release --verbose" -
Mixing absolute and relative children:
[tasks.build] run = ["frontend", "api.migrate"] # resolves to build.frontend and api.migrate
Show configured processes and (when using proc.toml) tasks:
oxproc list # human output
oxproc ls --json # machine output (includes task type and children)
oxproc list --names-only # names only (both processes and tasks)
oxproc list --tasks-only # only tasks (proc.toml only)This project is licensed under the MIT License.