Comparison · vs Healthchecks.io

RunWisp vs Healthchecks.io: a monitor, or a runner that monitors?

Healthchecks.io is a dead man's switch: your job pings a URL, and silence means trouble. RunWisp is the thing that runs the job, so it already knows the exit code, the duration and what got printed. They catch different failures, and the best setup often uses both.

Verdict

Should you switch?

The 10-second version. Skim, decide, scroll down for the receipts.

Use RunWisp if

  • You keep pasting a curl line onto every crontab entry, and keep forgetting one.
  • You want the full output of the failed run, not the first 100 kB of a ping body.
  • Retries, timeouts and overlap rules should happen, not just be noticed when they don't.
  • Your box can't make outbound calls: air-gapped, on-prem, strict egress.
  • You've run out of the 20 free checks and don't want a bill per job.
  • You also need long-running services restarted when they crash.

Keep Healthchecks.io if

  • You need to know when the whole server is dead. Nothing on the server can tell you that.
  • Your jobs run on platforms you can't install a daemon on: a managed host, a NAS, a router, CI.
  • You watch jobs across many machines from one dashboard.
  • You want phone calls, SMS or PagerDuty for a missed run.
  • The jobs already work and all you're missing is an alarm.
Head to head

Yes / no, at a glance

Yes / no — and both columns win on something.

Capability Healthchecks.io RunWisp
Runs the job ✗cron or systemd does ✓
Exit code per run ~if you ping /$? ✓always
Captured output ~POST body, 100 kB ✓full log, per run
Works with no job changes ✗curl per job ✓
Retries / timeouts / overlap policy ✗ ✓
Run history ✓ping log ✓
Alerts on failure ✓27 integrations ✓8 channels + inbox
Alerts on a job that never ran ✓ ~missed while daemon was down
Alerts when the whole host dies ✓ ✗pair with one check
One dashboard across many hosts ✓ ✗one daemon per host
Phone call / SMS alerts ✓paid credits ✗
Works with no outbound internet ~self-hosted on the LAN ✓
Open source ✓BSD-3 ✓GPL-3.0
Self-hostable ✓Django + DB ✓single binary
Free with no job limit ~20 checks hosted, unlimited self-hosted ✓
Long-running services ✗ ✓
Trade-offs

Pros & cons

What Healthchecks.io does better

  • Catches the failure nothing on the box can: power gone, kernel panic, network down, crond stopped.
  • Works with any scheduler on any machine: cron, systemd timers, Kubernetes, CI, a NAS, a router.
  • 27 integrations, including phone calls, SMS, WhatsApp, PagerDuty and Opsgenie.
  • One dashboard for every job on every machine, with projects and team roles.
  • Nothing to install on the server. A curl line and you're done.
  • Open source (BSD-3) with an official Docker image, and run by a one-person company that has kept the free tier generous.

What RunWisp adds

  • It runs the job, so “it ran” and “it succeeded” come from the process itself, not from a ping you hope fired.
  • Every run's full stdout and stderr, kept per task and searchable. No body size limit to plan around.
  • No curl to paste per job and nothing to forget: add a [tasks.*] stanza and history and alerts are on.
  • timeout, retry_attempts, on_overlap and catch_up change what happens, not just what gets reported.
  • Fails a run that printed ERROR but exited 0, with failures = ['+output:ERROR'].
  • Works fully offline. Run times and output never leave the machine.
  • A Run button, live logs and a TUI over SSH, so you can fix and re-run from the same place you noticed.
  • Supervises long-running services in the same file. Free with no per-job limit.
The simple case

The simple case

A nightly backup. On the left, cron runs it and the exit code goes to Healthchecks in the URL. On the right, RunWisp runs it and keeps the exit code, duration and output on its own.

crontab + Healthchecks.io
# crontab: cron runs it, Healthchecks.io waits for the ping
0 3 * * *  /usr/local/bin/backup.sh; \
    curl -fsS -m 10 --retry 5 -o /dev/null \
    https://hc-ping.com/<uuid>/$?
runwisp.toml
# runwisp.toml: the daemon runs it and records the result
[tasks.backup]
cron   = "0 3 * * *"
notify = ["telegram"]
run    = "/usr/local/bin/backup.sh"
Production-grade

The version that runs in prod

The setup we'd actually run. On the left, every job carries start and finish pings with its output in the body. On the right, RunWisp handles timeouts, retries and per-job alerts, and a single heartbeat task pings Healthchecks every five minutes. If the box dies, the pings stop and Healthchecks tells you. One check for the box instead of one per job.

crontab + Healthchecks.io (production)
# crontab: one check per job, start + exit-code pings, output in the body
0 3 * * *  RID=$(uuidgen); \
    curl -fsS -m 10 -o /dev/null "https://hc-ping.com/<uuid>/start?rid=$RID"; \
    OUT=$(timeout 55m /usr/local/bin/backup.sh 2>&1); \
    curl -fsS -m 10 --retry 5 -o /dev/null --data-raw "$OUT" \
      "https://hc-ping.com/<uuid>/$??rid=$RID"
# Period, grace time and alert channels are set per check
# in the Healthchecks dashboard.
runwisp.toml + one Healthchecks check
# runwisp.toml: RunWisp runs and alerts on every job;
# one Healthchecks check watches RunWisp itself.
[tasks.backup]
cron           = "0 3 * * *"
on_overlap     = "skip"
timeout        = "55m"
retry_attempts = 2
retry_backoff  = "exponential"
keep_runs      = 60
notify         = ["telegram"]
run            = "/usr/local/bin/backup.sh"

[tasks.heartbeat]
description  = "Tell Healthchecks.io this box is alive"
cron         = "*/5 * * * *"
catch_up     = 0
secrets_file = "/etc/runwisp/hc.env"   # HC_URL=https://hc-ping.com/<uuid>
run          = 'curl -fsS -m 10 --retry 3 -o /dev/null "$HC_URL"'
Migration

Migration cheat sheet

Healthchecks.io habits on the left, runwisp.toml on the right. Keep one check for the host.

Healthchecks.io RunWisp
; curl hc-ping.com/<uuid>/$? on each line Drop it. RunWisp records the exit code of every run.
/start + finish ping for duration Duration recorded per run, with CPU time and peak memory
POST body with the job's output Full stdout/stderr per run, in the Web UI and TUI
Cron schedule + timezone on the check cron = + timezone = on the task
Grace time timeout = "55m"; an overrun fails the run
Missing ping alert Firings missed while the daemon was down are recorded as missed and alert
Integrations (Slack, Telegram, ntfy, …) [notifiers.*]: Slack, Discord, Telegram, ntfy, Gotify, Pushover, email, webhook
Alert on a dead server Keep it: one heartbeat task pinging one Healthchecks check
Gotchas

Gotchas

RunWisp can't report its own death

RunWisp runs on the box it watches. It catches a failed run, a timeout, a crash, and firings missed while the daemon was down. If the machine loses power or network, nothing is left to send the alert. That is exactly what a dead man's switch is for. Keep one Healthchecks.io check fed by a RunWisp heartbeat task, ideally on the hosted service or another machine.

One check for the box, not one per job

With RunWisp handling per-job failures, Healthchecks only has to answer “is this machine alive?” That's one check per host instead of one per job, so the 20-check free plan covers 20 servers instead of 20 jobs.

Fewer alert channels

Healthchecks.io lists 27 integrations, including phone calls, SMS and on-call tools. RunWisp ships Slack, Discord, Telegram, ntfy, Gotify, Pushover, email and a generic webhook. For PagerDuty or Opsgenie, point the webhook notifier at their events API, with a template_path to shape the body.

Nothing to keep in sync

A heartbeat setup has two sources of truth: the crontab and the check's schedule in the dashboard. Change one and forget the other, and you get false alarms or silence. In RunWisp the schedule and the monitoring are the same line.

FAQ

FAQ

Is RunWisp a Healthchecks.io alternative?
Partly. RunWisp is an open-source cron replacement that ships as a single binary: it runs your jobs, records each run's exit code and output, and alerts on failures, so you don't need one heartbeat per job. What it can't do is notice that its own server died. Healthchecks.io can, because it watches from outside. The usual answer is both: RunWisp for the jobs, one Healthchecks check for the box.
Can I use RunWisp and Healthchecks.io together?
Yes, and we'd recommend it if total host failure matters to you. Add a heartbeat task to runwisp.toml that curls your check's ping URL every five minutes, with catch_up = 0. If the machine or the daemon stops, the pings stop and Healthchecks alerts you.
Can't Healthchecks.io show me the job's output too?
Yes, if you send it. Pings sent as POST store the body, and the hosted service keeps the first 100 kB (self-hosted defaults to 10 kB). You pipe the output into the ping yourself, or use a wrapper such as runitor. RunWisp captures stdout and stderr for every run with no wrapper, limited only by the per-task log_max_size you set.
Is Healthchecks.io free?
The hosted Hobbyist plan is free for 20 checks. Business is $20 a month for 100 checks. You can also self-host it for free: it is BSD-3-licensed Django with an official Docker image. RunWisp is GPL-3.0, free to self-host, with no limit on jobs.
Should I self-host Healthchecks.io on the same server as my jobs?
No. A monitor on the same box goes quiet at the same moment as everything else. Self-host it on a different machine, or use the hosted service. The same goes for RunWisp's heartbeat: it only helps if the check lives somewhere else.
Does RunWisp alert when a job doesn't run at all?
When the daemon was down at the scheduled time, yes: on the next start the firing is recorded as missed and goes through notify, and catch_up decides whether to run it late. When the machine never comes back, no. That case needs an outside monitor such as Healthchecks.io.
Get started
GPL 3.0 · no accounts · free to self-host

Let the runner watch the jobs. Let a heartbeat watch the runner.

Install the binary, move your crontab into runwisp.toml, and keep one Healthchecks.io check for the box. Every run lands with its exit code and output.

Install in 30 seconds
curl -fsSL https://get.runwisp.com | sh