homebase
A process supervisor and dashboard for the apps I self-host
My desktop hosts my own apps, like Job Tracker and Sentinel, and I reach them from anywhere over Tailscale. homebase is what keeps them running: it starts every app at boot, restarts anything that crashes, health-checks each one, and gives me a single dashboard with live logs and start, stop and restart buttons.
Adding a project is one entry in a JSON file. When I push changes to GitHub, one command pulls, rebuilds and redeploys an app on the desktop, or homebase itself. If something breaks, my phone gets a notification.
In Action

The real dashboard on my desktop: every app's state, response time, memory, restart count and last exit, with controls and live logs.
Under the Hood
0
third-party dependencies
~1.9k
lines of Go
36
test functions
2
platforms tested: Windows, macOS
How it works
- —One goroutine per app owns that app's process. Buttons and health checks send it requests over a channel instead of touching the process, so two restarts can never race to launch duplicate copies
- —Each app is a small state machine: stopped, starting, running, stopping, restarting after a crash, crashed, or external
- —External means something is already answering on the app's health URL, for example the old startup task. homebase reports it instead of starting a second copy that would fail on a port conflict
- —The dashboard can only start and stop apps listed in the config. There's no API for running arbitrary commands, and the buttons require a custom header, so another website can't press them for you
- —Secrets live in per-app env files outside the repo; the config itself is committed to git
{
"name": "jobtracker",
"command": "JobTracker.exe",
"dir": "C:\\Users\\Admin\\apps\\jobtracker",
"env_file": "C:\\Users\\Admin\\apps\\jobtracker\\jobtracker.env",
"health": "http://127.0.0.1:5206/",
"autostart": true,
"restart_when_unhealthy": true,
"source": {
"repo_dir": "C:\\Users\\Admin\\projects\\job-tracker",
"build": ["dotnet", "publish", "src\\JobTracker", "-c", "Release", "-o", "..."]
}
}Decisions that took more than one try
Apps outliving their supervisor
If homebase itself was force-killed, say by Task Scheduler or Task Manager, its apps kept running as orphans with no one watching them. The fix is a Windows Job Object: every app joins one job that's set to close with homebase, so Windows ends them automatically however homebase exits. I verified it on the desktop by force-killing homebase and checking that its app died with it.
The test suite runs real processes, on Windows too
The supervisor tests don't mock anything. The test binary re-runs itself as a tiny app that serves HTTP, crashes, or returns 500s, and the tests start, crash and kill it. I cross-compiled the same suite for Windows and ran it on the desktop, which is how I caught that Go refuses to run a program by relative path there.
Probing a site can get you banned by it
Sentinel, which homebase runs, used to probe for exposed files like /.env. On Parliament those paths are honeypots that ban the visitor's IP, so the first run banned my own IP address. Now any path a site treats as a trap goes in a skip list.
Deploys that can't take the apps down
Windows won't overwrite a running .exe, so an update stops the app through homebase, rebuilds, and starts it again, and a failed build brings the old version back up. homebase updates itself the same way: build the new exe, check it against the new config, and only then swap it in, keeping the previous version for a one-step rollback.
