Cron vs systemd Timers
Automate tasks with cron and systemd timers — Linux · networking · telemetry.
Focus: automate tasks with cron and systemd timers
You've just deployed a script that checks disk usage, but you can't trust yourself to run it every morning at 2 AM — and neither can your team. Manually launching maintenance scripts, report generators, or health checks is a recipe for missed deadlines and silent failures. That's why automating tasks with cron and systemd timers is a foundational skill for any Linux administrator or backend developer: it turns repetitive chores into self-sustaining services.
The problem this lesson solves
Routine system maintenance — log rotation, backup creation, cache purging, telemetry collection — frequently gets skipped when it depends on a human remembering to run a command. Worse, ad-hoc runs happen at unpredictable times, making output unreliable and debugging a nightmare. You need a dependable, scheduled execution mechanism that runs even when you're asleep, and that persists across reboots.
Cron has been the default for decades, but modern systemd-based distributions offer a more robust alternative: systemd timers. Both solve the same core problem — running a command at a defined time — but they differ radically in reliability, logging, and dependency handling. This lesson arms you with both tools so you can choose the right one for your use case and avoid the classic pitfalls that cause silent failures.
Core concept / mental model
Think of a job scheduler as a metronome for your system: it ticks at set intervals, triggering a repeated action. Cron is the old metronome — simple, JSON-like configuration, but no awareness of whether the previous tick actually finished. systemd timers are the new metronome — they integrate with the system's init process, so they know about dependencies, resource states, and can wake the machine from sleep to run a job.
- Cron reads a crontab file (per-user or system-wide) containing lines with five time fields (minute, hour, day of month, month, day of week) followed by a command. It's perfect for simple, interval-based schedules like “every day at 6 AM.”
- systemd timers are unit files (
.timer) that pair with a service unit (.service). They offer calendar events (e.g.,Mon..Fri 02:00:00), monotonic timers (e.g.,OnBootSec=10min), and persistence across reboots (Persistent=true). They also capture output in the journal, making debugging far easier.
Pro tip: When a Cron job fails, you often get a cryptic email or nothing at all. systemd timers log stdout/stderr to the journal — type
journalctl -u myjob.serviceto see exactly what happened.
How it works step by step
Understanding cron syntax
A crontab line has five fields, in order:
minute (0-59) hour (0-23) day-of-month (1-31) month (1-12) day-of-week (0-7, 0 and 7 = Sunday)
For example, 30 2 * * * /usr/local/bin/backup.sh runs backup.sh every day at 2:30 AM. Special strings like @daily or @reboot are shortcuts for common schedules.
systemd timer anatomy
A timer unit (e.g., /etc/systemd/system/backup.timer) declares when to trigger; a matching service unit (e.g., backup.service) defines what to run. Here's the key: the service unit should be oneshot (runs a single command) and does not need its own scheduling logic.
Step-by-step for cron
- Edit your crontab: run
crontab -e(edit) orcrontab -l(list) for your user. System-wide jobs go in/etc/crontabor/etc/cron.d/. - Add a line with the five fields and the absolute path to your script.
- Save and exit — cron automatically picks up the change.
- Verify by checking
/var/log/syslog(ormail) for cron logs.
Step-by-step for systemd timers
- Create a service unit that runs your script once.
- Create a timer unit that describes the schedule.
- Enable the timer (not the service!):
systemctl enable --now backup.timer. - Check status:
systemctl status backup.timerandsystemctl list-timers.
Hands-on walkthrough
Let's build both a cron and a systemd timer for the same task: a script that pings a remote host and logs the latency to a file. This is a classic telemetry pattern in the Linux · networking · telemetry track.
Step 0: Create the monitoring script
Create /usr/local/bin/ping_log.sh with the following content:
#!/bin/bash
# Simple ping latency logger
echo "$(date +'%Y-%m-%d %H:%M:%S') $(ping -c 1 example.com | grep time= | awk -F'time=' '{print $2}' | cut -d' ' -f1) ms" >> /var/log/ping_latency.log
Make it executable:
chmod +x /usr/local/bin/ping_log.sh
Step 1: Cron setup
# Edit your crontab
crontab -e
# Add this line to run every 5 minutes
*/5 * * * * /usr/local/bin/ping_log.sh
Wait a few minutes, then check the log:
cat /var/log/ping_latency.log
# Expected output (approx)
# 2025-05-01 10:05:00 12.3 ms
# 2025-05-01 10:10:00 14.1 ms
Step 2: systemd timer setup
Create /etc/systemd/system/ping-log.service:
[Unit]
Description=Log ping latency to file
[Service]
Type=oneshot
ExecStart=/usr/local/bin/ping_log.sh
Then create /etc/systemd/system/ping-log.timer:
[Unit]
Description=Run ping log every 5 minutes
[Timer]
OnCalendar=*:0/5
Persistent=true
[Install]
WantedBy=timers.target
Now enable the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now ping-log.timer
sudo systemctl list-timers | grep ping-log
To test the service manually without waiting for the timer:
sudo systemctl start ping-log.service
cat /var/log/ping_latency.log
Pro tip: Use
systemctl list-timers --allto see when each timer will fire next. This is invaluable for debugging schedule logic.
Compare options / when to choose what
| Feature | Cron | systemd Timers |
|---|---|---|
| Syntax | Simple five-field crontab | Calendar expressions or monotonic |
| Missing runs after downtime | No (unless with cronw or external tools) |
Yes with Persistent=true |
| Output logging | Mails to user (often unread) | Journal (systemd) |
| Dependencies | None | Systemd services, units |
| Environment variable control | Limited to crontab environment | Full unit environment |
| Random delay to spread load | Yes (e.g., @reboot + sleep) |
Yes (RandomizedDelaySec) |
| Resource sharing | No | Can share cgroups, This means you can't run two timers together easily |
Choose cron when: you need a quick, portable schedule with no complex dependencies and you're okay with minimal logging.
Choose systemd timers when: you need persistence across reboots, dependency-based triggering, rich logging, or integration with the systemd ecosystem (e.g., start after network-online.target).
Also consider tool-specific variations:
cronw/cronie: enhance cron with features like@rebootand job persistence.anacron: runs missed jobs after the machine is powered on (useful for daily rotation on desktops/laptops).at: for one-off scheduled tasks, not recurring.
Troubleshooting & edge cases
Cron job not running
- Check crontab syntax: run
crontab -land validate withcrontab -T(on some systems) or use an online checker. A stray space or wrong wildcard can silently disable a line. - Check the environment: cron uses a minimal PATH (often just
/usr/bin:/bin). Always use absolute paths for commands and scripts, and setPATHin the crontab if needed. - Look for cron logs: check
/var/log/syslogor/var/log/cronfor errors like "No MTA installed" — this often means cron couldn't send output and the job might have failed.
systemd timer not firing
- Did you enable the timer, not the service? Running
systemctl enable ping-log.servicewon't schedule anything; you need to enable the.timerunit. - Check timer status:
systemctl status ping-log.timershows if it's active. Usesystemctl list-timers | grep ping-logto see the next run. - Is the service unit in order? Use
systemctl start ping-log.servicemanually to ensure it works. If the service fails, the timer will keep trying and log errors in the journal. - Time zone issues: systemd timers use the local time zone by default; cron uses the system's time zone. If you're in a container or across time zones, be explicit.
- Persistent=true and random delay: If you set
Persistent=true, missed runs trigger immediately after boot. Combine withRandomizedDelaySec=5minto avoid a boot-time burst.
Script output missing
- For cron, redirect output to a file or use
MAILTOto receive errors:30 2 * * * /script.sh >> /var/log/script.log 2>&1. For systemd, checkjournalctl -u ping-log.service— it captures both stdout and stderr by default.
Avoiding duplicate runs
- If a job takes longer than the interval, use a lock file or
flockto prevent overlapping runs. Cron and systemd timers both start a new instance regardless of the previous one. Example:* * * * * flock -n /tmp/script.lock /script.sh.
What you learned & what's next
You now understand how to automate tasks with cron and systemd timers — from the core syntax and mental model to hands-on implementation and troubleshooting. You can compare the two approaches and choose the right one based on your needs: quick and simple cron, or robust and observable systemd timers.
You've achieved the learning objectives:
- Explain the core idea behind automating tasks with cron and systemd timers.
- Complete a practical exercise (ping logger) using both methods.
Your next step in the Linux · networking · telemetry track is to explore network service monitoring — how to actively poll endpoints and collect latency data over time. Automating tasks is the backbone; monitoring what those tasks produce is the next frontier. Keep building!
Practice recap
Now try it yourself: create a script that logs memory usage every 10 minutes using both cron and a systemd timer. Compare the outputs and notice how systemd timers give you richer logs in the journal. Next, set Persistent=true and see how missed runs are handled after a reboot — this will solidify the mental model of systemd timers as the modern choice for critical automation.
Common mistakes
- Forgetting to make the script executable: cron and systemd will fail silently if the script lacks the execute bit.
- Using relative paths inside the script — cron has a minimal PATH, so always use absolute paths or set PATH in the crontab. For systemd, set WorkingDirectory in the service unit.
- Forgetting to enable the systemd timer — you must run
systemctl enable --now timer.service, not just the service unit. - Ignoring overlap: if a job takes longer than the schedule interval, you risk multiple concurrent runs — use
flockor a lock file.
Variations
- Use
anacronto run jobs that were missed due to system downtime (e.g., on laptops). - Use
cronieorcronwfor enhanced cron features like@rebootsupport and better logging. - Use
atfor one-off scheduled tasks instead of recurring schedules.
Real-world use cases
- Automatically rotate and compress application log files every night using a systemd timer to avoid filling the disk.
- Schedule a nightly cron job to back up a database and upload the dump to an S3 bucket.
- Run a health check script that pings internal services every 5 minutes and alerts on failure via a webhook.
Key takeaways
- Understand the five-field cron syntax and special strings like
@dailyand@reboot. - Recognize the added reliability of systemd timers: persistence, dependency management, and built-in journal logging.
- Know when to choose cron (simplicity, portability) vs. systemd timers (robustness, integration).
- Always test scripts manually before scheduling, and use absolute paths to avoid environment pitfalls.
- Use
systemctl list-timersandjournalctlfor observability — they turn scheduling from black-box into debuggable. - Prevent overlapping runs with lock files or
flockto avoid race conditions in long-running jobs.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.