Skip to content

[defaults]

[defaults] is the one section in runwisp.toml that doesn’t describe something to run. Instead it sets fallback values that every [tasks.*] and [services.*] inherits, unless that task or service sets the field itself.

It’s here to save you from repeating yourself. Typing keep_for = "30d" and log_max_size = "100mb" onto twenty different tasks gets old fast — so put them in [defaults] once, and only bother overriding the few tasks that actually need a different value.

[defaults]
timeout = "1h"
log_max_size = "100mb"
log_on_full = "drop_old"
keep_runs = 50
keep_for = "30d"
healthy_after = "60s"
KeyDefaultWhat it does
timeout(unset)Default per-attempt wall-clock cap. Unset means no timeout. Applies to both tasks and services.
jitter(unset)Default jitter window for cron tasks; off when unset. Every cron task inherits it and joins the daemon-wide one-at-a-time gate — one line to smooth your whole schedule. Tasks only. See [tasks.*] jitter.
shell/bin/shDefault interpreter for run scripts. Absolute path. Per-task shell overrides. See [tasks.*] Working directory & shell.
stop_signal"SIGTERM"Default signal that opens the stop ladder before SIGKILL. One of SIGTERM, SIGINT, SIGQUIT, SIGHUP, SIGKILL, SIGUSR1, SIGUSR2. Per-task / per-service stop_signal overrides.
exit_codes[0]Default set of exit codes treated as success. Per-task / per-service exit_codes overrides. Codes are 0255.
log_max_size100MBDefault per-run log cap. Units b/kb/mb/gb/tb. Bare 0 and negatives are rejected.
log_on_full"drop_old"Default overflow policy: drop_new, drop_old, kill_task.
keep_runs(unset)Default row-count retention. Positive integer; hard internal cap of 1 000 000. Zero means “inherit from task-level setting” — only negatives are rejected.
keep_for(unset)Default age-based retention. Accepts days (d) and weeks (w) on top of the usual h/m/s/ms. Zero / negative durations are rejected, as are absurdly large values (over ~100 years) — a typo guard.
healthy_after"60s"Uptime a service instance must reach to count as healthy — resets its restart counter and clears the failed-start streak; faster failures count toward start_retries. Services only. Per-service override.
start_retries3Consecutive failed starts a service instance tolerates before going FATAL and stopping. Services only. Per-service override.
env(none)Inline KEY/VALUE map merged into every task and service. Task-level entries override on key collision. See [tasks.*] Environment & secrets.
env_file(none)Path to a dotenv file merged beneath [defaults.env]. Values are visible in the API/UI, like the rest of env.
secrets(none)Inline KEY/VALUE map merged into every task and service, but never shown in the API/UI. Task-level secrets override on key collision.
secrets_file(none)Path to a dotenv file merged beneath [defaults.secrets]. Only the path is visible; keys and values never leave the daemon.
notify_on_missedtrueWhether missed scheduled runs alert. Set false here to silence the alert daemon-wide; a per-task notify_on_missed still wins.

Some things you can’t default: cron, on_overlap, the retry_* keys, max_concurrent, queue_max, graceful_stop, and params. Those are too tied to a task’s intent — defaulting them would quietly change behaviour you meant to be explicit. Per-task graceful_stop just falls back to its built-in value when you leave it off, so set it only on the tables that need a different window.

jitter is the deliberate exception. It’s a pure load knob — it caps how far a run’s start may slip, never what it does or whether it fires — so a defaulted jitter can’t surprise you the way a defaulted cron or retry_* would. And pacing the whole schedule at once is exactly the job you want a single line for: drop jitter = "30m" in [defaults] and every cron task joins the same one-at-a-time gate, no per-task repetition.

The daemon-wide [daemon] shutdown_timeout is its own separate top-level setting that bounds the whole-daemon shutdown phase. Each task and service still gets its own graceful_stop window inside that cap, and if a graceful_stop runs longer than [daemon] shutdown_timeout, the daemon warns you about that task at boot.

For each task or service:

  1. If the field is set on the [tasks.*] / [services.*] table, use that.
  2. Else if it’s set in [defaults], use that.
  3. Else fall back to the built-in default (e.g. 100MB for log_max_size).

Leave a numeric retention field off at every level and you get no cap — runs just pile up until you set a value somewhere. Zero means “inherit from the layer above” for keep_runs (only negatives are rejected); keep_for rejects both zero and negatives. Out-of-range values are rejected at config load, so “I forgot to set a cap” can never quietly turn into “everything got deleted.”

[defaults]
timeout = "30m" # most tasks should die after 30 minutes
log_max_size = "50mb" # smaller default, override for noisy tasks
keep_runs = 100
keep_for = "30d"
healthy_after = "30s" # services that stabilise in 30s count as healthy
[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 longer
log_max_size = "500mb" # overrides default — output is large
graceful_stop = "20s" # this one needs more than the built-in 5s
run = "/usr/local/bin/export"
[services.flaky-worker]
healthy_after = "2m" # overrides default — this one takes longer to stabilise
run = "/usr/local/bin/worker"
  • 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, history_keep, etc.).
  • It doesn’t apply to [daemon] shutdown_timeout — that’s a daemon-wide setting, not a per-task value to inherit.
  • Notification routing (notify_on_failure, notify_on_success) has no defaulting layer — you set those channel lists per task. The notify_on_missed toggle is the exception: it’s a simple on/off, so it does inherit from [defaults] (see the table above).