oxproc
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.
Features
- 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.
Installation
- Install Rust: If you don't have Rust installed, get it from rust-lang.org.
- Clone the repository:
- Build the project:
The executable will be located attarget/release/oxproc.
Configuration
oxproc looks for a configuration file in the current directory in the following order:
1. proc.toml (Preferred)
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:
[]
= "python -m http.server 8000"
= "logs/web.out.log"
= "logs/web.err.log"
[]
= "while true; do echo 'Processing...'; sleep 2; done"
# stdout and stderr will default to worker.out.log and worker.err.log
2. Procfile (Fallback)
If 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).
Usage
Run oxproc from the directory containing your configuration file (proc.toml or Procfile).
Global option: --root
All commands accept --root <path> to point oxproc at a different project directory (where proc.toml/Procfile live). Defaults to current directory.
Examples:
Foreground (dev) mode
To monitor the output of all processes in real time (no daemon), run:
Press Ctrl+C to shut down children.
Daemon mode
Start a background manager that daemonizes and writes state under $XDG_STATE_HOME/oxproc/<project-id>/:
When 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):
Check status of the daemonized processes (alias: ps):
Stop all processes for this project (sends SIGTERM, then SIGKILL after a grace period):
Show log file locations or follow (combined view supported):
#### Colored prefixes
)
)
)
[\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/...).
Tasks (proc.toml only)
When using proc.toml, oxproc can run one‑off tasks defined under a [tasks] table.
Example proc.toml snippet:
[]
= "npm run dev"
[]
= "cargo build"
[]
= "cargo test"
Run tasks:
Notes
- 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.
Composite tasks (groups)
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).
Running multiple tasks
-
Sequential (default):
Example output:
▶ running build:frontend… …frontend logs… ▶ running build:api… …api logs… -
Parallel:
[] = ["frontend", "api"] = trueOutput is prefixed by child name, for example:
[build:frontend] … [build:api] … [build:frontend] … -
Forwarding arguments to all children:
# becomes: "pnpm … build --release --verbose" and "cargo build -p api --release --verbose" -
Mixing absolute and relative children:
[] = ["frontend", "api.migrate"] # resolves to build.frontend and api.migrate
List processes and tasks
Show configured processes and (when using proc.toml) tasks:
License
This project is licensed under the MIT License.