Skip to content

Best practices

RunWisp works without any of this. These are the settings that save you a bad morning later. Each one links to the page that explains it.

It is the whole definition of what runs on the host. With it in git, every change has an author and a reason, and a broken edit is one revert away. Keep secrets out of it (see below).

Add the #:schema line at the top, and your editor flags unknown keys and wrong types as you type. See Editor support and validation.

runwisp validate checks the file exactly like the daemon does and exits non-zero on any error. Run it in CI on every change to runwisp.toml. It also resolves ${VAR}, so set every variable the file uses, with placeholder values, in the CI job. A reload that fails validation changes nothing, but catching it in CI is earlier.

A timeout, a log size limit, and a history limit for every task belong in [defaults], not copied onto each task. A task that needs something else sets its own value.

Without one, a hung command runs forever, and with the default overlap rule every later run waits behind it. Set a default in [defaults] and raise it for the slow jobs. See timeout.

Probes and backups usually want on_overlap = "skip": a second copy helps nobody. Jobs where every run matters want the default queue. See Choosing a policy.

A health check missed during downtime tells you nothing now. Set catch_up = 0 on probes. Keep it on for jobs where each run handles its own slice of time.

Retry probes and API calls that fail on a network blip. Don’t retry a backup that failed halfway; fix the cause. See Retrying failed runs.

A task that runs every minute keeps thousands of runs. Set keep_runs or keep_for, and size [storage] for the machine.

Ten tasks at 0 3 * * * all start in the same second and fight over the disk and the database. Add jitter and they take turns.

Scheduled runs can’t ask for input. See Scheduled runs use the defaults.

A task’s graceful_stop should fit inside the daemon’s shutdown_timeout, or a restart cuts it off before it finishes cleaning up.

Load tokens and passwords with ${VAR}, ${file:...}, or secrets_file, and chmod 600 the files. See Storing the secret.

A daemon without RUNWISP_PASSWORD makes up a new one on every start and logs everyone out. runwisp service install sets one up for you. See The password.

RUNWISP_AUTH=off is for a daemon bound to loopback or an isolated network. See Running without a password.

Before you open the port beyond the host, put it behind a reverse proxy and set trusted_proxies, or turn on RunWisp’s own TLS. See Reverse proxies.

A token in a URL ends up in proxy logs and CI output. Send it in the Authorization header, and give a caller that can only use a URL its own token so you can rotate it alone. See Hooks.

See Keeping it private.

The bell works with no setup, but nobody watches it at 3am. Add one channel for every task with global_notifiers.

Find out about a typo in a webhook URL now, not during the first real outage. See Testing a channel.

A task on a laptop that sleeps misses runs on purpose. Take missed out of its failures; see Missed runs.

A task that starts a container uses docker run --rm, never -d, so RunWisp sees its output and exit code. See Start a container with docker run.

When RunWisp itself runs in Docker, mount the directory that holds runwisp.toml. Many editors save by replacing the file, and a single-file mount keeps showing the container the old copy. See Volumes.