Added
- PHP SDK,
runwisp/runwispon Packagist: define tasks in plain PHP, or run the Laravel, Symfony or CodeIgniter 4 scheduler under RunWisp withrunwisp:work.#[RunWisp\Task]on a command, job or closure adds task keys such asretry_attempts, andrunwisp:checklists the tasks with their next runs and validates the config before you deploy. runwisp import systemdreads.timerunits and turns each timer and the service it starts into one scheduled task.- A hook
runtakestail=Nand returns the last N lines of the run’s log with the run, so a CI job holding only a hook token can show why a run failed. runwisp start,stopandrestarttake--token(orRUNWISP_HOOK_TOKEN) with--url, so a task that runs as another user can restart a service through its hook token.runwisp statusandrunwisp listtake--urland--passwordto ask a remote daemon, andRUNWISP_URLnow applies to them too.- Runs record how many lines they printed. The TUI run list has an OUTPUT column and the Web UI’s task page a third bar next to CPU and RAM, both amber when a run printed far more or far less than the task usually does, such as a silent backup that printed a warning.
- A task’s run durations are colored like CPU and RAM in the TUI run list and the Web UI’s task page: amber from 1.5× the task’s usual run, red from twice it. The TUI task details show the usual duration.
[daemon] heartbeat_urlpings a dead man’s switch such as healthchecks.io every minute, so you hear about it when the daemon itself is down.- A
recoverednotification tells you when a task succeeds after a failed run. It goes wherever the task’s failures go, the bell included.
Changed
runwisp demostarts on a free port when 9477 is taken, unless you passed--port.- The Web UI shows times in 24-hour format everywhere, including cron schedules (“At 03:15”) and run list dates.
- A service that exits 0 before
healthy_aftercounts as a failed start, so a command that daemonizes goes FATAL instead of restarting forever. Its run log says a service must stay in the foreground. runwisp service uninstalllists the tasks and services that stop running before it asks, and puts back the crontab linesrunwisp promotecommented out.runwisp takeoversaves a stable Web UI password the wayservice installdoes, instead of leaving one that changes on every restart.runwisp runends with a line saying how the run ended, such asTask failed reason=timeout duration=6s exit=1.- Tasks that start at the same second are reported in one warning line, and any notifier now answers the warning about a crontab’s
MAILTO. runwisp import supervisordwith no file reads/etc/supervisor/supervisord.confor/etc/supervisord.conf, andrunwisp import cronwith nothing on stdin says what to pipe in.runwisp import cronnotes jobs that redirect their output to a file, since RunWisp can’t show that output.runwisp listshows each service’s live state, such asFATAL, andrunwisp statuslists the services that gave up restarting.- The update check is skipped when the
CIenvironment variable is set, so a daemon in a test pipeline never contacts concierge.runwisp.com. - A compose service whose container already runs from
docker compose upattaches to that container instead of starting a second one, and runs its own container from the next restart. - A run killed by a signal RunWisp didn’t send names the signal in its log, also when the shell reports it as exit 128+n (such as 137), and a
SIGKILLpoints at the kernel’s out-of-memory killer. runwisp takeoversays to runsudo runwisp reloadaftercrontab -e, since an edit waits for a reload.runwisp import cronsuggests a[services.*]table for an@rebootjob that never exits, and a job that writes its output to a file keeps a comment saying so above its table, also afterrunwisp promote.runwisp import systemdfinds a unit by name, such asbackup.timer, in the systemd unit directories, and with nothing on stdin it says what to pass instead of importing an empty service.- An unknown key like
retryssuggests the key you likely meant (retry_attempts). - A service now tries 10 failed starts in a row before it goes FATAL, about 5 minutes with the default backoff, instead of 3. Set
restart_attempts = 3to keep the old limit.
Fixed
- Scheduled tasks run on time after the system clock jumps (
date -s, an NTP step, a VM snapshot restore or a laptop waking up), instead of waiting out the old interval. runwisp restartandrunwisp service installno longer count toward systemd’s crash-loop limit, so restarting a few times in a row no longer hands the jobs back to cron.@runwisp/jsin an Astro app under pnpm finds the runwisp binary in the built server instead of failing at startup with “could not run runwisp”.@runwisp/jsin a Nuxt app on thenode-clusterpreset stops its tasks on SIGTERM and lets running ones finish, instead of hanging for 30 seconds and killing them.withRunWisp()fails the build withoutput: "export", where the tasks would never run.- The “could not run runwisp” error says when
@runwisp/jswas bundled into the app and that it belongs underdependencies. nuxi previewkeeps the data of a Nuxt app using@runwisp/jsin the project’s.runwisp, not in.output, which the next build deletes.@runwisp/jsapps with many fast tasks no longer slow the daemon down: the app asks for next run times at most once a second instead of after every run.- A task that throws a value that can’t be printed (its
stackormessagegetter throws) fails its run instead of crashing the app. - A task that prints in a tight loop no longer takes down every run in the app. Output is sent in batches, and past 256 MB waiting to be sent, that task’s lines are dropped with a note in its log.
- A task file’s process (
schedule(cron, "./job.js")) getsSIGKILLoncegraceful_stopruns out, stop signals reach the processes it started, and they all exit when the app dies. - A run the app skips because the previous callback ignored its stop signal is recorded as
skipped, notfailed. - Under Bun, an app crash from an unhandled promise rejection writes its stack trace into the logs of the runs in progress, as under Node.js.
task()andservice()reject a missing callback,schedule()rejects a task file that doesn’t exist, and registering a task afterclose()throws instead of being silently ignored.close()logs what it is waiting for when a callback keeps running.- The Overview no longer overflows the screen on phones when a task has a long name, and a long group name no longer spills out of its card.
- A long task name no longer squeezes the header search box until its shortcut hint overlaps the badges beside it.
- A timed-out run no longer shows “Exit -1” on the Overview, and its task reads “Last run timed out” instead of “Last run failed”.
- Run lists show statuses like “Start failed” instead of “Start_failed”.
- A filtered run list that matches nothing says so and offers to clear the filters, instead of “No runs yet”.
- The “end of output” marker stays visible after a very long line, and a run that printed nothing says so instead of “Waiting for logs…”.
- Dates in another year include the year, so a next run “in 1 year (Feb 29)” names which February.
- The Notifications page shows its name in the header.
- Notifications no longer say “exit -1” for a killed run or “after 0s” for a run under a second, and number service instances from #1.
runwisp service statusno longer reports a healthy system service as degraded withLinger: OFF; linger only applies to--localservices.- Importing a crontab installed with
crontab -no longer turns cron’s# DO NOT EDIT THIS FILEheader into the first job’s description. - Importing a crontab no longer turns a commented-out job above a live one into its description, and
runwisp import cronflags a missing user column only when the sixth field is a real account that isn’t also a program (find,rsync,certbot). - The Web UI no longer logs a blocked JavaScript
evalin Firefox on first load. - A task the kernel kills for running out of memory no longer stops the whole systemd service and every other task with it. Run
sudo runwisp service installagain to update an existing unit. runwisp restartchecks the config first and leaves the daemon running when the config is invalid, instead of stopping it for one that can’t start.- Live logs and Web UI updates stream through nginx as they happen, instead of arriving in one burst.
- A non-root
runwisp listorrunwisp runon a box with the system service says to use sudo, instead of failing on a missingrunwisp.toml. - Opening a dropdown menu in the Web UI no longer scrolls the page. Thanks @dahool.
- A failure that comes back after a successful run alerts again and gets a new bell row, instead of being held in the earlier failure’s
coalesce_window. runwisp takeoverends with the upgrade command for the binary the service runs, not for the copy in your home directory.- Errors from the daemon, like a rejected
runwisp reload, print the reason instead of the raw JSON response. - The Web UI shows a task’s next run in the timezone its schedule uses, so “next run in 7 hours (02:30)” agrees with “At 02:30”.
runwisp service install --localno longer calls the password file it saved root-owned.runwispwithout a terminal and without arunwisp.tomlfails right away with a hint, instead of leaving an empty.runwispdirectory behind.
- v1.9.0 latest
- v1.8.0
- v1.7.1
- v1.7.0
- v1.6.0
- v1.5.1
- v1.5.0
- v1.4.0
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.1
- v1.0.0
- v0.16.4 pre
- v0.16.3 pre
- v0.16.2 pre
- v0.16.1 pre
- v0.16.0 pre
- v0.15.1 pre
- v0.15.0 pre
- v0.14.0 pre
- v0.13.2 pre
- v0.13.1 pre
- v0.13.0 pre
- v0.12.0 pre
- v0.11.0 pre
- v0.10.0 pre
- v0.9.0 pre
- v0.8.0 pre
- v0.7.0 pre
- v0.6.0 pre
- v0.5.0 pre
- v0.4.0 pre
- v0.3.0 pre
- v0.2.0 pre
- v0.1.2 pre
- v0.1.0 pre