Comparison · vs Dagu
RunWisp vs Dagu: not every job needs a DAG.
Dagu orchestrates multi-step pipelines with dependencies, conditionals, and parameter passing. RunWisp schedules independent tasks and babysits long-running services. Different tools, different problems. Pick the one that matches your workload.
Should you switch?
The 10-second version. Skim, decide, scroll down for the receipts.
Consider RunWisp if
- Every job on your box is independent. No step B waits on step A's output.
- You also need long-running services supervised alongside scheduled tasks.
- DST handling, overlap policies, and catch-up after downtime matter more than DAG edges.
- You want per-run history with stdout/stderr captured automatically.
- A TUI over SSH is part of your workflow.
Stay on Dagu if
- Any of your jobs form a pipeline: extract then transform then load.
- Steps pass outputs to downstream steps via parameters.
- Conditional execution matters: skip step C if step B produced no rows.
- You need the DAG visualization in the web UI to reason about your workflow.
- You're building data pipelines, not running cron jobs.
Yes / no, at a glance
Yes / no, both columns win on something.
| Feature | Dagu | RunWisp |
|---|---|---|
| Single binary, no deps | | |
| Step dependencies (DAG) | core feature | |
| Conditional execution | preconditions | |
| Parameter passing between steps | output vars | |
| Cron scheduling | | 5/6-field + aliases |
| Long-running services | batch only | [services.*] |
| Run history per firing | | SQLite rows |
| Captures stdout / stderr | | |
| Retries with backoff | retryPolicy | constant / linear / exp |
| Failure notifications | email | Slack / Discord / webhook / + |
| Overlap policy | ~ skip if running | skip / queue / allow |
| Catch-up after downtime | | latest / all / skip |
| DST handling | | deduped / next-valid |
| Web UI | DAG graph view | run history + logs |
| TUI | | |
| DAG visualization | | |
| Config format | ~ YAML | ~ TOML |
Pros & cons
What Dagu does better
- DAG execution is the whole point. Steps depend on each other, run conditionally, pass outputs downstream.
- Preconditions let you skip or abort a step based on the output of a previous one.
- The web UI renders the DAG as a graph. You can see which step failed and why the downstream steps never ran.
- Parameters and templating let you build reusable workflow definitions.
- Sub-DAG execution: one DAG can call another DAG as a step.
- If your workload is a pipeline, Dagu was built for it. RunWisp was not.
What RunWisp adds
- Long-running services alongside scheduled tasks in one config file and one process tree.
- DST handling as named behaviour: fall-back deduped, spring-forward fires at the next valid minute.
-
catch_up = "latest"/"all"/"skip"after downtime. Dagu skips missed runs. -
on_overlap = "skip"/"queue"/"allow"per task. - Coalesced failure notifications. Five flaps become one Slack message, not five.
-
runwisp tuistreams run history and logs over SSH. No browser needed. - Simpler config for the simple case. If every job is independent, a flat TOML stanza per task is less ceremony than a YAML DAG.
The simple case
Run a backup script. Dagu defines a single-step DAG. RunWisp defines a scheduled task. For one independent job, the DAG wrapper is ceremony.
# backup.yaml — a single-step Dagu DAG
steps:
- name: backup
command: /usr/local/bin/backup.sh # runwisp.toml
[tasks.backup]
cron = "0 3 * * *"
run = "/usr/local/bin/backup.sh" Production-grade
A four-step ETL pipeline. Dagu expresses the dependency chain: extract, validate, transform, load. RunWisp can run the same four scripts, but they fire independently on the same schedule. If step order matters, Dagu is the right tool here.
# etl-pipeline.yaml — extract, transform, load with dependencies
schedule: "0 4 * * *"
params:
- DATE: "{{ .Now.Format "2006-01-02" }}"
steps:
- name: extract
command: /opt/etl/extract.sh
output: RAW_FILE
- name: validate
command: /opt/etl/validate.sh
depends:
- extract
preconditions:
- condition: "$RAW_FILE"
expected: re:.*\.csv$
- name: transform
command: /opt/etl/transform.sh $RAW_FILE
depends:
- validate
output: CLEAN_FILE
- name: load
command: /opt/etl/load.sh $CLEAN_FILE
depends:
- transform
retryPolicy:
limit: 3
intervalSec: 30 # runwisp.toml — same four scripts, but no dependency chain
# RunWisp can't express "load depends on transform".
# If your ETL steps MUST run in order, keep Dagu.
# If they're truly independent, RunWisp is simpler:
[tasks.extract]
cron = "0 4 * * *"
timeout = "30m"
keep_runs = 60
notify_on_failure = ["slack-ops"]
run = "/opt/etl/extract.sh"
[tasks.validate]
cron = "0 4 * * *"
timeout = "10m"
keep_runs = 60
run = "/opt/etl/validate.sh"
[tasks.transform]
cron = "0 4 * * *"
timeout = "45m"
retry_attempts = 3
retry_delay = "30s"
retry_backoff = "exponential"
keep_runs = 60
run = "/opt/etl/transform.sh"
[tasks.load]
cron = "0 4 * * *"
timeout = "20m"
retry_attempts = 3
retry_delay = "30s"
retry_backoff = "exponential"
keep_runs = 60
notify_on_failure = ["slack-ops"]
run = "/opt/etl/load.sh" Migration cheat sheet
Only migrate if your DAGs are actually independent jobs. If steps depend on each other, keep Dagu.
| Dagu | RunWisp |
|---|---|
schedule: in the YAML | cron = on each [tasks.*] |
steps[].command | run = |
steps[].depends | No equivalent. If jobs are independent, drop it. If not, keep Dagu. |
steps[].output: VAR | No equivalent. Write to a file and read it back if needed. |
steps[].preconditions | Check inside the script itself, exit non-zero to fail. |
retryPolicy.limit | retry_attempts = |
retryPolicy.intervalSec | retry_delay = + retry_backoff = |
mailOn: failure | notify_on_failure = ["slack-ops"] |
| Dagu web UI (DAG graph) | RunWisp web UI (flat run history) or runwisp tui |
params: with Go templates | Environment variables in env = { ... } or inline in the script |
Gotchas
RunWisp does not do DAGs
This is the big one. If step B depends on step A finishing successfully, RunWisp cannot express that. Tasks fire on their own schedule, independently. Wrapping a pipeline in a single shell script works for simple chains but loses Dagu's per-step visibility, retry, and conditional logic. If you need a DAG engine, keep the DAG engine.
No parameter passing between tasks
Dagu's output: lets step A export a variable that step B reads. RunWisp tasks are isolated. The workaround is a shared file or environment variable, but that's on you to manage. If your workflow relies on output chaining, that's a reason to stay on Dagu.
Dagu is batch-only; RunWisp does services too
If you also need to keep a web server, a worker, or a daemon alive alongside scheduled jobs, RunWisp handles both in one config. Dagu is a workflow engine for batch executions. Long-running processes need a separate supervisor.
Independent jobs get simpler config
If your Dagu DAGs are all single-step (one steps: entry, no depends:), the YAML wrapper adds ceremony. A flat [tasks.backup] stanza with cron = and run = is fewer lines and easier to scan.
FAQ
Whenever your jobs form a pipeline. If step B needs step A's output, if you want to skip step C conditionally, if you need to visualize the dependency graph, Dagu is the right tool. RunWisp schedules independent tasks. It is not a workflow engine and does not pretend to be.
You can put a shell script that runs extract.sh && transform.sh && load.sh into a single run = field. It works, but you lose per-step visibility, per-step retry, conditional branching, and the DAG graph. For anything beyond two commands in sequence, a real DAG engine is the better choice.
No. RunWisp is deliberately not a workflow engine. Tasks are independent by design. If you need DAGs, use Dagu, Airflow, Temporal, or another tool built for that problem. RunWisp focuses on doing the simple case well: scheduled independent tasks and long-running services.
Yes. They don't share state, ports (unless you configure the same web UI port), or config files. Use Dagu for your pipelines and RunWisp for your independent cron-style tasks and services.
Long-running service supervision (restart with backoff, graceful stop, replicas), DST-aware scheduling, catch-up after downtime, overlap policies (skip / queue / allow), coalesced failure notifications, and a TUI. These are features for recurring independent jobs, not workflows.
Not at all. Dagu is also a single Go binary with no dependencies. The setup experience is similar. The difference is what you configure: Dagu wants YAML DAG files with steps and dependencies. RunWisp wants a TOML file with flat task and service stanzas. Pick the shape that matches your workload.
Dagu supports email notifications on DAG success or failure. RunWisp supports Slack, Telegram, Discord, generic webhooks, and email, with coalesced alerts so five rapid failures become one message. If your team lives in Slack, RunWisp's notification story is broader.
Both are lightweight Go binaries. RunWisp idles around 25 MB of RAM. Dagu is similarly lean. Neither will stress a 512 MB VPS. The choice should be about the workload shape, not resource usage.
More comparisons: RunWisp vs cron · RunWisp vs PM2 · RunWisp vs Ofelia .
Not every job needs a DAG. Some just need to run on time.
If your tasks are independent, a flat TOML stanza per job is all you need. Install the binary, write a schedule, watch it land in the dashboard.