How they differ at a glance
| Task Scheduler (Windows) | Cron (Linux/Unix) | |
|---|---|---|
| Configuration | XML files + GUI | Plain text crontab files |
| Triggers | Schedule, event, logon, idle, startup, connection… | Only schedule (and @reboot) |
| Conditions | On AC power, network available, idle time, … | None built-in |
| Multiple actions per task | Yes | One line = one action (chain in script) |
| Run as another user | Stored credentials | Owner of the crontab |
| Run with highest privileges | Checkbox | Run as root |
| Missed runs | "Run task as soon as possible" option | Cron doesn't; anacron or systemd timers do |
| History/logs | Event Viewer | syslog, journalctl, or your own log file |
Trigger mapping
"On a schedule" → cron expression
| Task Scheduler | Cron equivalent |
|---|---|
| Daily at 9:00 AM | 0 9 * * * |
| Weekly Mon at 9:00 AM | 0 9 * * 1 |
| Weekly Mon, Wed, Fri | 0 9 * * 1,3,5 |
| Monthly day 1 | 0 0 1 * * |
| Daily, repeat every 5 minutes for 1 hour | */5 9-9 * * * (runs minute 0,5,10…55 of hour 9) |
| Every 15 minutes | */15 * * * * |
"At log on" → not cron's domain
Cron has no concept of user login. Use:
~/.bashrcor~/.profilefor per-user-shell-start actions- systemd user services with
--userand a custom target /etc/profile.d/*.shfor system-wide login actions
"At startup" → @reboot
@reboot /usr/local/bin/my-startup-script.sh
Or for system-level startup, prefer a proper systemd unit file. @reboot works but doesn't give you start-order dependencies, retries, or restart-on-failure.
"On an event" → no direct mapping
Cron is time-based only. For event-based triggering, look at:
inotifywaitfor filesystem events- systemd path units (
.pathfiles) for file watches - systemd socket activation for network events
- Message queues (RabbitMQ, Redis pub/sub) for application events
"On idle" → custom logic
Cron doesn't watch system load. If you need "run when idle," check load average inside your script:
0 2 * * * [ $(cat /proc/loadavg | cut -d. -f1) -lt 2 ] && /path/to/script.sh
Action mapping
Task Scheduler allows multiple actions per task. Cron is one line, one command. To chain:
0 2 * * * /path/to/step1.sh && /path/to/step2.sh && /path/to/step3.sh
Or — much better — wrap in a single script:
0 2 * * * /path/to/nightly-pipeline.sh
The script then handles step ordering, error checking, and logging in one place.
Send email action
Task Scheduler's deprecated "send email" maps to mail, sendmail, or msmtp from a script. For modern workflows, send via a webhook to Slack/Discord/email-service instead.
Conditions and settings
"Start only if computer is on AC power"
On laptops, check the battery state inside the script:
if grep -q 'Discharging' /sys/class/power_supply/BAT*/status 2>/dev/null; then echo "On battery — skipping" >&2 exit 0 fi # … real work …
"Start only if any network connection is available"
if ! ping -c 1 -W 5 8.8.8.8 > /dev/null 2>&1; then echo "No network — skipping" >&2 exit 0 fi # … real work …
"Stop the task if it runs longer than X"
Use timeout from coreutils:
0 2 * * * timeout 30m /path/to/script.sh
timeout 30m kills the job if it doesn't finish in 30 minutes.
"If the task fails, restart every N minutes up to M times"
Wrap with a retry loop. Or migrate to systemd, which has OnFailure= hooks built in.
Migration recipe
Take an existing scheduled task (e.g., "Run backup.bat daily at 2:00 AM, only on AC power, retry once if it fails"):
Step 1: Identify the trigger → daily at 2 AM → 0 2 * * *
Step 2: Rewrite the action — backup.bat becomes backup.sh (you'll need to port the logic). If it's a complex script, consider what shell features you need: variables, conditionals, control flow are all available in bash.
Step 3: Add the conditions inside the script (AC power, network, etc.) since cron has no built-in conditions.
Step 4: Add retry logic if needed:
#!/bin/bash
for attempt in 1 2; do
if /usr/local/bin/run-backup; then
exit 0
fi
echo "Attempt $attempt failed, retrying..." >&2
sleep 60
done
exit 1
Step 5: Install the crontab entry:
crontab -e # Add the line: 0 2 * * * /path/to/wrapped-backup.sh >> /var/log/backup.log 2>&1
Step 6: Add monitoring (see our monitoring guide) so you actually know whether it's running.
What cron can't do (and what to use instead)
| Task Scheduler feature | Cron equivalent |
|---|---|
| Run on filesystem change | inotifywait in a wrapper, or systemd path units |
| Run on log on | Shell rc files or systemd user services |
| Wake the computer to run | RTC wake (rtcwake) called from another job |
| Run as another user with stored password | sudo -u other-user with a sudoers rule, or rewrite as a systemd service running as that user |
| Multiple triggers per task | Multiple crontab lines pointing to the same script |
| "Run task as soon as possible after a missed schedule" | anacron or systemd timer with Persistent=true |
For anything that needs the richer trigger model of Task Scheduler, the modern answer is systemd timers + units — they map almost 1:1 to Task Scheduler features and beat cron in every category except universal-availability. See our cron vs systemd guide.