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.
The one-liner
Section titled “The one-liner”Pipe your current crontab in, or point it at a file — both work the same way:
crontab -l | runwisp import cron # whatever's installed for yourunwisp import cron /etc/crontab # a specific fileThat prints an annotated runwisp.toml to your terminal and a summary of what
it did. Given a crontab like:
SHELL=/bin/bashPATH=/usr/local/bin:/usr/binMAILTO=ops@example.com
# Nightly database backup30 2 * * * /usr/local/bin/backup.sh --full@reboot /opt/app/warmup*/5 * * * * /opt/healthcheck.shyou 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 = truerun = "/opt/app/warmup"
[tasks.healthcheck]cron = "*/5 * * * *"run = "/opt/healthcheck.sh"When it looks right, save it:
crontab -l | runwisp import cron -o runwisp.tomlrunwisp 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.
How the pieces map
Section titled “How the pieces map”Most of a crontab translates one-to-one. Here’s the whole picture:
| crontab | runwisp.toml | Notes |
|---|---|---|
30 2 * * * cmd | cron = "30 2 * * *" + run | The five fields are copied verbatim. |
@daily, @hourly, @weekly | cron = "@daily" … | RunWisp speaks the same descriptors. |
@reboot cmd | run_on_start = true + run | Runs once each time the daemon starts. |
@midnight, @annually | @daily, @yearly | Normalized to RunWisp’s spelling. |
| user column (system crontab) | user = "..." | Detected automatically; force with --system. |
SHELL=/bin/bash | [defaults] shell | Must be an absolute path. |
PATH=…, other VAR=val | [defaults.env] | Applied to every task. |
CRON_TZ=… / TZ=… | [scheduler] timezone | Becomes the daemon-wide timezone. |
# a comment above a job | description = "..." | 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.
What needs a human
Section titled “What needs a human”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_failureat it — it’s strictly better than a wall of cron mail you’ve long since filtered to a folder.@reboot. Mapped torun_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.sh→backup). They’re just labels — rename any that aren’t obvious, and the full command is preserved either way.
What you get that cron never gave you
Section titled “What you get that cron never gave you”- 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.