RunWisp vs Jobber: same itch, different reach.
Both replace cron with a Go binary. One stops at scheduled jobs; the other adds services, a web dashboard, and notifications that don't require sendmail.
Should you switch?
The 10-second version. Skim, decide, scroll down for the receipts.
Switch to RunWisp if
- You want run history you can browse in a web UI, not just grep in syslog.
- Notifications should hit Slack or Telegram, not depend on a local sendmail.
- You also supervise long-running services on the same box.
- Overlap policies and catch-up after downtime matter.
- One daemon per system beats one daemon per user.
- You need the same binary on macOS, WSL, and Docker.
Stay on Jobber if
- Per-user isolation is a hard requirement (multi-tenant box, each user owns their jobs).
- You want the lightest possible footprint: no embedded SQLite, no web server.
- Every job result should pipe into your own notifyProgram script — RunWisp's notifiers are six fixed drivers plus a webhook.
- One YAML file at ~/.jobber and three error policies is all the model you want.
Yes / no, at a glance
Yes / no — and both columns win on something.
| Capability | Jobber | RunWisp |
|---|---|---|
| Run history per firing | ~jobber log, syslog | ✓SQLite rows + Web UI |
| Captures stdout / stderr | ✗ | ✓ |
| Retries with backoff | ~Backoff on error only | ✓constant / linear / exp |
| Failure notifications | ~sendmail or program | ✓Slack / Telegram / SMTP / sendmail |
| Coalesced alerts (no notify storms) | ✗ | ✓ |
| Overlap policy | ✗ | ✓skip / queue / kill |
| Catch-up after downtime | ✗ | ✓latest / all / skip |
| DST handling | ✗ | ✓deduped / shifted |
| Web UI / dashboard | ✗ | ✓ |
| TUI over SSH | ✗ | ✓ |
| Long-running services | ✗ | ✓[services.*] |
| Per-user daemon isolation | ✓one daemon per user | ✗system-wide |
| Six-field cron (seconds) | ✓ | ✓optional seconds field |
| Single static Go binary | ✓ | ✓ |
| macOS / WSL / Docker | ~Linux + macOS | ✓ |
Pros & cons
What Jobber still does better
- Per-user daemon model: each Unix user owns their jobs with no shared config or shared daemon.
- Lighter footprint: no embedded SQLite, no web server, no TUI. Just runs jobs.
- Simple mental model: one YAML file at
~/.jobber, three error policies, done. - Longer track record in the cron-replacement niche.
What RunWisp adds
- Every firing is a row with status, duration, exit code, and captured stdout/stderr.
- Web dashboard and TUI for browsing run history without SSH + grep.
- Six-field cron with an optional seconds field; Jobber's leading column carries over unchanged.
- Slack, Telegram, Discord, email, and webhook notifications without requiring a local MTA.
- Overlap policies (
skip/queue/kill) are a config field, not your problem. catch_up = 1replays missed firings after a reboot.- Services (always-on processes) and tasks (scheduled) in one daemon.
- DST fall-back fires once (deduped), spring-forward fires once, shifted forward by the gap.
- One binary on Linux, macOS, WSL, and Docker. Same TOML everywhere.
The simple case
Nightly backup at 03:00. Both are a few lines in a config file.
# ~/.jobber
[jobs]
- name: nightly-backup
cmd: /usr/local/bin/backup.sh
time: '0 0 3 * * *'
onError: Stop # runwisp.toml
[tasks.nightly-backup]
cron = "0 3 * * *"
run = "/usr/local/bin/backup.sh" The version that runs in prod
Same backup, hardened. notifyProgram scripts on the left; named fields and built-in notifications on the right.
# ~/.jobber, hardened for production
[jobs]
- name: nightly-backup
cmd: /usr/local/bin/backup.sh
time: '0 0 3 * * *'
onError: Backoff
notifyOnError:
- type: program
path: /usr/local/bin/notify-slack.sh
notifyOnFailure:
- type: program
path: /usr/local/bin/page-ops.sh # runwisp.toml, hardened for production
[tasks.nightly-backup]
description = "Snapshot the data volume to off-site storage"
cron = "0 3 * * *"
on_overlap = "skip" # never two backups at once
timeout = "55m" # die before the next firing
retry_attempts = 2
retry_delay = "30s"
retry_backoff = "exponential"
keep_runs = 60 # two months of history
notify = ["slack-ops"]
run = "/usr/local/bin/backup.sh" Migration cheat sheet
Jobber concept on the left, RunWisp equivalent on the right.
| Jobber | RunWisp |
|---|---|
~/.jobber (per-user YAML) | runwisp.toml (system-wide TOML) |
time: '0 0 3 * * *' (6 fields) | cron = "0 0 3 * * *" (6-field accepted) or drop the seconds column |
cmd: | run = |
onError: Stop | retry_attempts = 0 (default) |
onError: Backoff | retry_attempts = N + retry_backoff = "exponential" |
onError: Continue | Default behaviour; failures are logged, next firing proceeds. |
notifyOnError: [{type: program}] | notify = ["slack-ops"] |
jobber log | Web UI run list / runwisp tui |
jobber reload | runwisp reload (validates first, no restart) |
| No overlap handling | on_overlap = "skip" |
| No catch-up after reboot | catch_up = 1 (default) |
Gotchas
Six-field cron carries over directly
Jobber's time field has a leading seconds column: 0 0 3 * * *. RunWisp now accepts the same six-field form, so the expression copies over unchanged; the five-field form (0 3 * * *) and @every 30s work too. Quartz operators (L, W, #) are not parsed.
No per-user daemon model
Jobber runs one daemon per Unix user, each reading ~/.jobber. RunWisp is system-wide. If per-user isolation matters (shared hosting, multi-tenant boxes), you lose that boundary.
Jobber's onError: Backoff is not the same as retry_backoff
Jobber's Backoff error policy delays the next scheduled firing, not the current one. RunWisp's retry_attempts + retry_backoff retries the same firing immediately with increasing delays. They solve different problems.
Tasks run as the daemon's user unless you say otherwise
Jobber jobs run as the user whose ~/.jobber they live in. RunWisp tasks run as the daemon's user by default; when the daemon runs as root, set user = "deploy" per task to run as another uid.
FAQ
Can RunWisp run alongside Jobber while I migrate?
Jobber runs per user. Can RunWisp do that?
Does RunWisp support Jobber's six-field cron syntax?
How do Jobber's error policies map to RunWisp?
Does RunWisp need sendmail for notifications?
Is RunWisp heavier than Jobber?
Can RunWisp supervise long-running services too?
Does the same RunWisp binary work on macOS and WSL?
Outgrown ~/.jobber? One binary, full history.
Install RunWisp, translate your YAML jobs to TOML stanzas, and watch every firing land in the web dashboard.
curl -fsSL https://get.runwisp.com | sh