June 4, 2026
Why one binary
Most process supervisors ask you to bring infrastructure. supervisord needs Python. PM2 needs Node. Airflow needs a metadata database, a message broker, and a weekend. RunWisp ships as a single static binary — no runtime, no interpreter, no container required.
This wasn’t a flex. It was a design constraint that fell out of the problem.
The target machine
RunWisp is built for the box you SSH into: a VPS, a Raspberry Pi, a Docker container, a CI runner. These machines have disk and a shell. They may not have Python 3.11, or Node 20, or a Postgres instance you’d trust with scheduler metadata.
A single binary means curl | sh works on a fresh Alpine container the same way it works on a five-year-old Ubuntu LTS. No package manager, no dependency resolution, no “but it works on my machine.”
Embedding SQLite
Run history — exit code, duration, captured stdout/stderr — needs to live somewhere. A flat file gets unwieldy after a few thousand runs. An external database is another thing to operate.
modernc.org/sqlite is a pure-Go translation of SQLite. No CGo, no system library, no apt install libsqlite3-dev. It compiles into the binary and opens a file on disk. Queries are SQL. Backups are cp. The database sits in the data directory alongside run logs, protected by the same file permissions.
The tradeoff: the binary is larger (~25 MB) than it would be with a flat-file store. And SQLite’s single-writer lock means RunWisp can’t shard writes across cores. Neither matters at the scale this tool targets — dozens of tasks, not thousands.
Embedding the dashboard
The web UI is a Svelte app, built at compile time and embedded via Go’s embed directive. When the daemon starts, it serves the dashboard from memory. No static file directory to deploy, no CDN to configure, no CORS headers to debug.
The TUI is built with Bubble Tea — same data, different surface. SSH into a box, run runwisp tui, and you’re looking at run history and live logs without opening a browser.
Both UIs are read-only plus trigger. They can start a task early or inspect a run, but they can’t mutate runwisp.toml. The config file is the source of truth; the UIs are windows into it.
What it costs
Honest accounting:
- Binary size. ~25 MB stripped. Larger than a purpose-built cron wrapper. Smaller than a Node runtime.
- Compile time. Pure-Go SQLite adds ~30 seconds to a clean build. CI caches make this invisible in practice.
- No plugin system. Everything ships in the binary. You can’t drop in a custom notifier without recompiling. Notifications (Slack, Telegram, email) are built in; if yours isn’t, file an issue.
- Single-writer SQLite. Fine for one daemon managing dozens of tasks. Not fine if you wanted to run ten daemons writing to the same database (don’t do this).
The bet
The bet is that operators would rather scp one file than debug a dependency graph. That a 25 MB binary that starts in under a second and idles at 25 MB of RAM is cheaper to operate than a lighter binary plus its runtime. That “it just works on a fresh box” is worth the compile-time cost.
Seven releases in, nothing has made us regret it.