Skip to content

Migrating from cron

Cron runs your jobs, but it doesn’t tell you anything. A job fails at 3am and the first you hear of it is a missing file the next morning. RunWisp keeps the exact same schedules and turns every one of them into something you can watch: per-run history, captured stdout/stderr, exit codes, and a notification when something breaks.

You don’t have to hand-translate your crontab. runwisp import cron reads it and writes the runwisp.toml for you.

Pipe your current crontab in, or point it at a file — both work the same way:

Terminal window
crontab -l | runwisp import cron # whatever's installed for you
runwisp import cron /etc/crontab # a specific file

That prints an annotated runwisp.toml to your terminal and a summary of what it did. Given a crontab like:

SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin
MAILTO=ops@example.com
# Nightly database backup
30 2 * * * /usr/local/bin/backup.sh --full
@reboot /opt/app/warmup
*/5 * * * * /opt/healthcheck.sh

you get:

[defaults]
shell = "/bin/bash"
[defaults.env]
PATH = "/usr/local/bin:/usr/bin"
[tasks.backup]
description = "Nightly database backup"
cron = "30 2 * * *"
run = "/usr/local/bin/backup.sh --full"
# @reboot — runs once each time the daemon starts.
[tasks.warmup]
run_on_start = true
run = "/opt/app/warmup"
[tasks.healthcheck]
cron = "*/5 * * * *"
run = "/opt/healthcheck.sh"

When it looks right, save it:

Terminal window
crontab -l | runwisp import cron -o runwisp.toml
runwisp validate

-o writes the file (it won’t clobber an existing runwisp.toml without --force), and runwisp validate confirms the result parses before you start the daemon. Then runwisp daemon — and the same jobs run on the same schedules, except now they’re all on the dashboard.

Most of a crontab translates one-to-one. Here’s the whole picture:

crontabrunwisp.tomlNotes
30 2 * * * cmdcron = "30 2 * * *" + runThe five fields are copied verbatim.
@daily, @hourly, @weeklycron = "@daily"RunWisp speaks the same descriptors.
@reboot cmdrun_on_start = true + runRuns once each time the daemon starts.
@midnight, @annually@daily, @yearlyNormalized to RunWisp’s spelling.
user column (system crontab)user = "..."Detected automatically; force with --system.
SHELL=/bin/bash[defaults] shellMust be an absolute path.
PATH=…, other VAR=val[defaults.env]Applied to every task.
CRON_TZ=… / TZ=…[scheduler] timezoneBecomes the daemon-wide timezone.
# a comment above a jobdescription = "..."The comment becomes the task’s description.
MAILTO=…(nothing — see below)RunWisp notifies instead of emailing.

RunWisp’s cron grammar is a superset of vixie-cron’s: the standard five fields, descriptors like @daily, and a few extras (six-field specs with seconds, @every 30s). How scheduling works has the details.

The importer never drops anything silently — anything it can’t map cleanly shows up as a # TODO in the file and a note in the summary. The usual ones:

  • MAILTO. Cron emails job output; RunWisp doesn’t. Instead it captures output for every run and can alert you on failure through Slack, Telegram, email, or a webhook. Wire one up and point your tasks’ notify_on_failure at it — it’s strictly better than a wall of cron mail you’ve long since filtered to a folder.
  • @reboot. Mapped to run_on_start, which fires once each time the daemon starts (not the machine). That’s usually what you wanted; double-check if you were relying on true boot timing.
  • A % in a command. In crontab, % means a newline / start-of-stdin. RunWisp hands your command to the shell verbatim, so review any job that used it.
  • The task name. Names are derived from the command (/usr/local/bin/backup.shbackup). They’re just labels — rename any that aren’t obvious, and the full command is preserved either way.
  • History. Every fire is a run with a start time, duration, exit code, and captured output — browsable in the Web UI and TUI, queryable over REST.
  • Failure that’s loud. A non-zero exit (or a missed tick while the daemon was down) can notify you instead of vanishing.
  • Overlap control. A slow job that’s still running when its next tick arrives no longer silently double-runs — pick queue, skip, or terminate.
  • Manual runs. Trigger any job by hand from the dashboard or runwisp exec <name> without editing the schedule.

Once the daemon is running and you’re happy, retire the crontab with crontab -r (back it up first). From then on runwisp.toml is the one place your schedule lives.