Skip to content

Configuration overview

Everything RunWisp does is configured in one file: runwisp.toml. It’s the sole source of truth for what runs and how — the REST API and Web UI can read and trigger, but they never touch your task definitions. Edited the file? Run runwisp reload (or send SIGHUP) to pick it up; a few daemon-wide settings need a full restart.

The file breaks into a handful of sections, and most of them are optional. You’ll usually want at least one [tasks.*] or [services.*] table with a run key — otherwise there’s nothing for the daemon to run.

# Disk-usage safeguards
[storage]
max_size = "5gb"
min_free_space = "500mb"
# Global defaults applied to every task unless overridden
[defaults]
timeout = "1h"
log_max_size = "100mb"
log_on_full = "drop_old"
keep_runs = 50
keep_for = "30d"
[tasks.backup-db]
group = "Backups"
description = "Nightly database backup"
cron = "0 2 * * *"
timeout = "30m"
on_overlap = "skip"
keep_runs = 30
run = "pg_dump mydb | gzip > /backups/mydb-$(date +%F).sql.gz"
[tasks.process-event-queue]
description = "Worker that retries with exponential backoff"
cron = "*/10 * * * *"
on_overlap = "queue"
retry_attempts = 3
retry_delay = "2s"
retry_backoff = "exponential"
run = "/usr/local/bin/process-queue"
[services.metrics-daemon]
description = "Always-on metrics collector"
run = "/usr/local/bin/metrics-agent"

Run runwisp in a directory that doesn’t have a runwisp.toml yet and it’ll offer to write a minimal starter for you — the Quick start walks through that.

SectionPurposeReference
[storage]Disk-usage limits — caps total bytes used and reserves headroom on the data partition.[storage]
[daemon]Daemon-wide settings — shutdown budget, public URL for notification links, the metrics endpoint.[daemon]
[scheduler]Scheduler-wide settings — the default timezone every cron expression is evaluated in.Timezone
[defaults]Defaults inherited by every task and service unless explicitly overridden.[defaults]
[tasks.<name>]Scheduled or manually-triggered units of work. Cron expression, concurrency, retries, timeout, retention, per-execution parameters.[tasks.*]
[services.<name>]Always-on processes. One or more instances, exponential restart backoff, graceful shutdown.[services.*]
[compose.<alias>]Imports services from an existing docker-compose.yml so each becomes an observable RunWisp service.[compose.*]
[[notifier]]Declares one outbound channel. Repeated for each channel.Notifications model
[[notification_route]]Sends events (run.failed, run.timeout, …) to one or more channels. One rule, many tasks.Notification rules
[notify]Global notification settings — bell channels, retry budget, history retention, coalescing.Global settings
notify_on_failureSets the channels to notify when one specific task ends failed, timeout, or crashed.Per-task notifications

One rule cuts across every section: any string value can pull from an env var or a file with ${VAR} / ${file:path}${...} substitution has the details.

Edit runwisp.toml, then run runwisp reload (or send the daemon a SIGHUP) to pick up the change without bouncing the process. Tasks get added, changed, and removed live; in-flight runs finish under the definition they started with. It’s always explicit — RunWisp never watches the file and reloads on its own. The reload is validate-first, so a typo can’t take your scheduler down, and a few daemon-wide settings still need a full restart. The Reload page has the details.

For everything reload won’t do live — changing [storage], the scheduler timezone, the bind port, or upgrading the binary — restart instead: stop the daemon, make the change, and run runwisp daemon again (or bounce it through whatever supervisor is managing the process). In-flight runs are cancelled on the way down — they end as stopped if they bow out within the [daemon] shutdown_timeout window, or get reconciled as crashed on the next boot if they don’t — then the database reopens and the schedule comes back up from the new file.

One thing to watch: a parse error at startup kills the boot. The daemon exits non-zero before it ever opens its port, and by then your previous instance is already gone. The safe pre-flight is to run runwisp validate against the new file before you restart. (A bad edit picked up by runwisp reload is safe by contrast — it’s rejected and the running daemon keeps going.)

What the schema considers a breaking change

Section titled “What the schema considers a breaking change”

This is pre-1.0, so any release might change the TOML schema, the REST API, or the on-disk layout — and an upgrade may wipe run history. Anything like that gets flagged in the CHANGELOG; the semver guarantees kick in at 1.0.