Tasks and services
RunWisp runs two kinds of work. A task runs and exits: a backup, a report, a health probe. A service is meant to stay up: a queue worker, an agent, a server. If the command exits on its own when it’s done, it’s a task. If it runs until you stop it, it’s a service.
[tasks.backup-db]cron = "0 3 * * *"run = "pg_dump app | gzip > /backups/app.sql.gz"
[services.worker]instances = 2run = "node worker.js"If you find yourself writing while true; do ...; done in a task, make it a
service (or see a polling loop below).
What’s different
Section titled “What’s different”Tasks are started by something: a cron schedule,
a manual run, or an API call. Only tasks have scheduling
(catch_up,
jitter,
run_on_start),
parameters, overlap rules,
and retries.
Services start with the daemon and, by default, are restarted when they exit. Only
services have instances,
restart,
healthy_after, and boot ordering
(autostart,
priority,
depends_on).
Putting a key on the wrong kind is a config error. Everything else (logs,
timeouts, environment, failures,
notify) works the same on both, and every
task run and every service instance shows up in run history with its exit code
and output.
Tasks and services share one set of names, so a task and a service can’t both
be called worker.
Running several copies
Section titled “Running several copies”A task uses max_concurrent: the most
runs that may overlap. A service uses
instances: how many copies are always up.
max_concurrent = 4on a thumbnail task that runs every minute. Most runs finish in seconds, but during a backlog up to four run at once.instances = 4on a queue worker. Four processes stay connected to the queue, and RunWisp restarts any one that exits.
Get this backwards and it goes wrong in a predictable way. A long-lived worker written as a task holds its slot while the queue backs up. A 30-second job written as a service gets restarted over and over.
Choosing on the edge cases
Section titled “Choosing on the edge cases”A polling loop
Section titled “A polling loop”while true; do work; sleep 60; done is usually better as a task with
cron = "* * * * *" and on_overlap = "skip". Each poll then gets its own run,
exit code, and log, so you can see exactly which one failed.
Keep it a service when the process holds state worth keeping between rounds: a queue connection, a websocket, a cache it built in memory.
A job you only start by hand
Section titled “A job you only start by hand”Still a task. Leave out cron and start it with
runwisp run <name>, the Run Task button in the Web UI, the TUI, or the
REST API.
A task that must never overlap
Section titled “A task that must never overlap”Tasks already queue by default: a new run waits for the previous one to finish.
If two runs must never even line up, use on_overlap = "skip". See
Overlapping runs.