execmgr is a small execution manager for local apps and scripts.
Think of it as a very lightweight process manager for things you don’t want to daemonize properly yet, but want to keep track of.
No magic. No background services. Just folders, scripts, and a lockfile.
I wanted something that:
- can start / stop local scripts without thinking about PIDs
- remembers what I ran last and how many times
- keeps logs per app automatically
- doesn’t require systemd, Docker, or writing a service file every time
- is predictable and hackable
So this exists.
Click to view
This is the fastest way to see execmgr actually do something.
cargo build --release
cp target/release/execmgr ~/.local/bin/execmgr create demoThis creates a directory at ~/.local/state/execmgr/demo/.
Open the generated start script:
$EDITOR ~/.local/state/execmgr/demo/start.shPut a simple loop in it:
#!/bin/sh
while true; do
echo "demo app running at $(date)"
sleep 2
doneexecmgr run demoThe app now runs in the background. execmgr uses a file lock to make sure you don't accidentally start it twice.
execmgr status demoexecmgr log demo -fTo use the stop.sh script (write exit logic in the stop.sh):
execmgr stop demoTo just nuke the process via PID:
execmgr kill demoEach app is just a directory with a few files:
execmgr/
└── myapp/
├── app.json # metadata (created time, run count, last pid)
├── app.lock # used by flock to check running
├── start.sh # main entrypoint
├── stop.sh # cleanup script
└── logs/
├── stdout.log
└── stderr.log
When you run an app, execmgr wraps your start.sh in a bash subshell that manages a file lock. If the lock is held, the app is "running." If the process dies, the lock is released automatically by the OS.
State is stored using XDG conventions:
EXECMGR_HOME(override)$XDG_STATE_HOME/execmgr$HOME/.local/state/execmgr.execmgr(fallback)
execmgr create <name>Creates the folder and boilerplate start.sh/stop.sh.
execmgr ls # list all apps
execmgr ls -l # detailed list
execmgr ps # list all running apps
execmgr ps -l # detailed ps- run / start: Runs the
start.shdetached. Logs are truncated (reset) on every run. - stop: Runs the
stop.shscript. Use this if your app needs a graceful shutdown (likepodman-compose down). - kill: Sends a
kill -9to the last known PID. Use this when your script is stuck. - status: Full metadata dump for a specific app.
execmgr log <name> # view stdout
execmgr log <name> --stderr # view stderr
execmgr log <name> -f # stdout: tail -f
execmgr log <name> -f --stderr # stderr: tail -f
execmgr log <name> -c # clear logs
execmgr log <name> -c --stderr # clear only stderr log
execmgr log <name> -c --stdout # clear only stdout logexecmgr info # see total apps, running count, and binary paths
execmgr rm <name> # delete the app folder (refuses if running)
execmgr rm -f <name> # delete the app folder and skip confirmation (refuses if running)- Locking: Uses
flockvia a wrapper. This is much more reliable than checking if a PID exists, as PIDs get reused by the OS. - Environment:
start.shandstop.share executed in their respective app directory. - No Restart Policy: If your script crashes, it stays dead. This isn't
systemd. It's a basic manager. - Logs:
execmgrredirects stdout/stderr to files. It does not rotate logs; they are wiped every time yourunthe app.
MIT.
Do whatever you want with it. If it breaks, you get to keep both pieces.