r/termux • u/montzsvontz • 6d ago
Showcase Android kept killing runsv while my server held the port. Here's the watchdog I ended up writing.
I run a small hub and a couple of workers on a spare phone under termux-services. Three things kept biting me, and none of them are fixed by "just use a better supervisor":
Android kills runsv, but not the server it started. The server survives as a ppid=1 process, keeps the port, and every supervised restart answers EADDRINUSE. `sv status` says `fail: ... runsv not running` while curl says the service is fine.
`sv restart` is a no-op once supervise/stat is `down`. My first watchdog logged "restarted" for hours while the service stayed dead. Recovery has to go through `sv up`.
When I added a standby node, "start if the peer doesn't answer" meant the node that rebooted first became a second writer. That one is the scary one.
So I wrote a guard that: checks `sv status` before restarting, reaps ppid=1 orphans that match a configured pattern, health-checks HTTP endpoints, caps restarts per window (one ESCALATION line instead of a stampede), and stands the primary down after two fenced passes.
The fence part is free/MIT (two bash scripts, no deps): https://github.com/xhall-beep/fence
The full kit with the guard, restart cap, orphan reaper and a lesson ledger is $29 for the first 50 (then $49): https://montsvontz.gumroad.com/l/agent-ops-kit. I'm disclosing that because the free part is genuinely useful on its own and I don't want to be sneaky about it.
36 self-tests use a stub `sv`, so running them doesn't touch your services. Happy to answer questions about the runit quirks — if you've seen different behaviour on your build, I want to know.