[defaults]
[defaults] is the one section in runwisp.toml that doesn’t define
something to run. It defines fallback values that every [tasks.*]
and [services.*] inherits unless they override the field themselves.
It exists because writing the same keep_for = "30d" and
log_max_size = "100mb" on twenty tasks gets tedious. Pull those into
[defaults] once, override per-task only when the value is different.
Available keys
Section titled “Available keys”[defaults]timeout = "1h"log_max_size = "100mb"log_on_full = "drop_old"keep_runs = 50keep_for = "30d"| Key | Type | Built-in default | What it does |
|---|---|---|---|
timeout | duration | 0 (no limit) | Default per-attempt wall-clock cap. Go duration string. |
log_max_size | byte size | 100MB | Default per-run log cap. Units b/kb/mb/gb/tb. |
log_on_full | enum | "drop_old" | Default overflow policy: drop_new, drop_old, kill_task. |
keep_runs | int | 0 (no row cap) | Default row-count retention. -1 means explicit unlimited; positive N keeps N. |
keep_for | duration | 0 (no time cap) | Default age-based retention. Accepts d/w/h/m units. "unlimited" opts out. |
There is no default cron, no default on_overlap, no default
retry_*. Those vary per task by intent; defaulting them would hide
behaviour.
Precedence
Section titled “Precedence”For each task or service:
- If the field is set on the
[tasks.*]/[services.*]table, use that. - Else if it’s set in
[defaults], use that. - Else fall back to the built-in default (
100MB,drop_old,0, etc.).
Both retention fields are tri-state, so the precedence is unambiguous
even when [defaults] sets a value:
Per-task keep_runs | Effect |
|---|---|
omitted or 0 | Inherit [defaults] keep_runs; if that’s also 0, no row-count cap. |
-1 | Explicit unlimited — overrides any positive default. |
N (positive) | Cap at N. Wins over the default. |
Per-task keep_for | Effect |
|---|---|
omitted or "" | Inherit [defaults] keep_for; if that’s also unset, no time-based cap. |
"unlimited" | Explicit unlimited — overrides any positive default. |
"30d" (duration) | Delete runs older than this. Wins over the default. |
So a task that wants to opt out of an inherited cap writes
keep_runs = -1 / keep_for = "unlimited", not 0 / "" (which
would silently use the default).
Worked example
Section titled “Worked example”[defaults]timeout = "30m" # most tasks should die after 30 minuteslog_max_size = "50mb" # smaller default, override for noisy taskskeep_runs = 100keep_for = "30d"
[tasks.heartbeat]cron = "*/5 * * * *"run = "/usr/local/bin/heartbeat"# inherits 30m timeout, 50mb log cap, 100 runs, 30d retention
[tasks.nightly-export]cron = "0 2 * * *"timeout = "4h" # overrides default — exports take longerlog_max_size = "500mb" # overrides default — output is largerun = "/usr/local/bin/export"
[tasks.audit-log]cron = "0 0 * * *"keep_runs = -1 # explicit unlimited — override defaults' 100keep_for = "unlimited" # also opt out of the inherited 30d windowrun = "/usr/local/bin/audit"What [defaults] doesn’t do
Section titled “What [defaults] doesn’t do”- It doesn’t apply to
[storage]— that’s a global cap, not a per-task default. - It doesn’t apply to
[notify]settings (coalesce_window,queue_size, etc.). - Notification routing (
notify_on_failure,notify_on_success) has no defaulting layer — you set it per task.
Where to next
Section titled “Where to next”[tasks.*]reference — the per-task overrides this defaults.[services.*]reference — same.[storage]reference — the daemon-wide disk safeguards (separate from per-tasklog_max_size).