fleet_man
A lightweight Python library for running and monitoring scripts on Raspberry Pis from a browser. Each Pi hosts its own control panel with live log streaming. An optional fleet coordinator aggregates all Pis into a single dashboard.
Built on FastAPI + WebSockets. No external message broker. No database. Each Pi is fully autonomous.
install
pip install 'uvicorn[standard]' fastapi httpx
quick start — single Pi
# app.py
import sys
import uvicorn
from pathlib import Path
from process_manager import ProcessManager, ScriptSpec
PY = sys.executable
scripts = {
"CAN-M": ScriptSpec("CAN Manager", [PY, "gc_monitor/run.py"]),
"backup": ScriptSpec("backup", ["bash", "jobs/backup.sh"]),
"server": ScriptSpec("api server", [PY, "jobs/server.py"]),
}
mgr = ProcessManager(
name="pi-kitchen",
scripts=scripts,
log_dir=Path("/var/log/jobs"),
port=5001,
)
app = mgr.create_app()
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=5001)
Open http://<pi-ip>:5001 in your browser.
ProcessManager
ProcessManager(
scripts, # dict[str, ScriptSpec] — required
name="pi", # node name shown in UI
log_dir=Path("/tmp/pm_logs"), # where .log files are written
coordinator_url=None, # optional fleet coordinator URL
host=None, # LAN IP advertised to coordinator (auto-detected)
port=8000, # port this Pi listens on
)
ScriptSpec
ScriptSpec(label, cmd)
| arg | type | description |
|---|---|---|
label |
str |
display name shown in the UI |
cmd |
list[str] or callable |
command to run |
cmd can be a callable — it is invoked at start time, so it can pull config from disk or environment dynamically:
def build_cmd():
config = load_config()
return ["python", "worker.py", "--env", config["env"]]
ScriptSpec("worker", build_cmd)
fleet coordinator
The coordinator runs on any central machine (laptop, NAS, another Pi). It polls each registered Pi, tracks online/offline status, and serves a unified fleet dashboard where you can start and stop scripts across all nodes.
# coordinator.py
import uvicorn
from coordinator import create_coordinator_app
app = create_coordinator_app(fleet_name="home-lab")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=9000)
Open http://<coordinator-ip>:9000.
auto-registration
Pass coordinator_url to ProcessManager and each Pi registers itself on startup and re-registers every 30 seconds — no manual steps needed. Boot order doesn't matter; Pis retry until the coordinator is reachable.
mgr = ProcessManager(
name="pi-kitchen",
scripts=scripts,
coordinator_url="http://192.168.1.10:9000",
port=5001,
)
You'll see confirmation in the Pi's console:
[pm] registered with coordinator at http://192.168.1.10:9000
If the coordinator is down at boot:
[pm] coordinator unreachable at http://192.168.1.10:9000 — will retry
Either way the Pi starts normally. The coordinator is purely additive — removing it changes nothing about how the Pi operates.
manual registration (curl)
If you're not using coordinator_url, you can register a node manually:
curl -X POST http://192.168.1.10:9000/register \
-H "Content-Type: application/json" \
-d '{"name":"pi-kitchen","host":"192.168.1.42","port":5001}'
log files
Each script writes stdout and stderr to a .log file in log_dir. For a script with key CAN-M the log is written to <log_dir>/CAN-M.log. Logs persist across restarts and are also tailable directly:
tail -f /var/log/jobs/CAN-M.log
The last 500 lines are buffered in memory and replayed instantly when you open the log panel in the browser. New lines stream in live via WebSocket.
API reference
Each Pi exposes the following endpoints:
| method | path | description |
|---|---|---|
| GET | / |
local dashboard UI |
| GET | /api/status |
JSON status of all scripts |
| POST | /api/start/<key> |
start a script |
| POST | /api/stop/<key> |
stop a script |
| GET | /api/log/<key> |
last 500 lines of log (plain text) |
| WS | /ws |
status push every 2s + start/stop commands |
| WS | /ws/<key> |
live log stream for one script |
The coordinator exposes:
| method | path | description |
|---|---|---|
| GET | / |
fleet dashboard UI |
| POST | /register |
register or re-register a node |
| GET | /api/fleet |
full fleet status JSON |
| GET | /api/nodes |
all node info |
| POST | /api/<node>/start/<key> |
start a script on a specific node |
| POST | /api/<node>/stop/<key> |
stop a script on a specific node |
| WS | /ws |
live fleet status push + forwarded commands |
architecture
[ browser ]
│
│ WebSocket (live status + logs)
│
├─────────────────────────┐
│ │
[ coordinator :9000 ] [ Pi dashboard :5001 ]
│ │
│ HTTP poll /api/status │ asyncio subprocess
│ every 3s │ stdout/stderr → WS
│ │
└──────── Pi nodes ────────┘
pi-kitchen :5001
pi-garage :5001
pi-shed :5001
Each Pi is standalone. The coordinator is optional. If the coordinator restarts, Pis re-register automatically within 30 seconds.
notes
- Scripts must run in the foreground. Processes that daemonize (fork and detach) will not be tracked correctly.
- State is in-memory. If the Pi's
process_managerprocess restarts, running scripts become orphans. Use systemd to keepprocess_manageritself alive if uptime matters. sys.executableis recommended over"python"or"python3"to ensure the correct interpreter is used, especially inside a virtualenv.
Release files for fleet-man 0.0.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fleet_man-0.0.4.tar.gz | 18.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fleet_man-0.0.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 37.2 kB
Release files / fleet_man-0.0.4.tar.gz
| Download URL | fleet_man-0.0.4.tar.gz |
|---|---|
| Size | 18.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
916ca2cf1d198fac861e6048764d39a116c499b1517cd8a3fda835b6adb3e019
|
|
BLAKE2b-256 checksum How to use checksums |
702fff8bd85011fcb8ba27b3cc115696bb569b916831e5737cad5a5692ba4219
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / fleet_man-0.0.4-py3-none-any.whl
| Download URL | fleet_man-0.0.4-py3-none-any.whl |
|---|---|
| Size | 18.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d9fef5927bf570cb4ae2e041704eb8156faff2dab6de321bdf2128bb810131b5
|
|
BLAKE2b-256 checksum How to use checksums |
cda836fa91572ff0f03dacffee8c124dba7a7c98e951dc994310f60165215dc5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|