When managing periodic script execution on Linux systems with systemd, administrators face a fundamental choice between using timer units or service unit restart directives. Both approaches can achieve similar timing patterns, but with important technical and operational differences.
Systemd timers provide the most robust solution for periodic execution. They offer:
- Precise scheduling capabilities with calendar semantics
- Proper dependency handling between units
- Persistent state tracking (LastTriggerUSec)
- Flexible failure handling through OnFailure dependencies
# Example timer unit (example.timer)
[Unit]
Description=Run example every 5 minutes
[Timer]
OnCalendar=*:0/5
AccuracySec=1min
Persistent=true
[Install]
WantedBy=timers.target
Using Restart=always with RestartSec provides a simpler but more limited approach:
- No external timer unit required
- Immediate restart capability (no wait for next schedule)
- Simplicity for always-running services
# Example service unit with restart (example.service)
[Unit]
Description=Example periodic service
[Service]
Type=simple
ExecStart=/usr/local/bin/example-script
Restart=always
RestartSec=300
[Install]
WantedBy=multi-user.target
Factor | Timer | Restart |
---|---|---|
Scheduling Precision | High (nanosecond) | Low (second) |
Execution Guarantee | Calendar-based | Process-based |
Failure Handling | Separate unit | Built-in |
Logging | Separate entries | Continuous |
Resource Usage | Lower | Higher |
For frequent executions (e.g., every minute), timers show significant advantages:
- Lower CPU overhead (no constant process monitoring)
- No accumulation of zombie processes
- Better integration with systemd's job management
For most periodic script scenarios, systemd timers represent the superior solution because:
- They properly model the scheduling intent
- They integrate better with systemd's ecosystem
- They provide more monitoring capabilities through systemctl
The restart approach should be reserved for:
- Services that must remain running continuously
- Quick recovery scenarios where process restart is preferred
- Legacy compatibility requirements
Converting from cron to systemd timers:
# Original cron entry
*/5 * * * * /usr/local/bin/backup-script
# Equivalent timer unit
[Unit]
Description=Backup timer
[Timer]
OnCalendar=*-*-* *:0/5:00
Unit=backup.service
[Install]
WantedBy=timers.target
When automating periodic tasks in Linux, developers often face the choice between:
- Traditional cron jobs
- Systemd service units with Restart directives
- Systemd timer units
For a script needing periodic execution (e.g., every 5 minutes), here's how each approach differs:
1. Systemd Service with Restart
[Unit]
Description=My Periodic Script
[Service]
ExecStart=/usr/local/bin/myscript.sh
Restart=always
RestartSec=300
2. Systemd Timer Unit
[Unit]
Description=Run myscript every 5 minutes
[Timer]
OnUnitActiveSec=5m
OnBootSec=5m
[Install]
WantedBy=timers.target
Execution Timing Behavior
The Restart approach will:
- Run immediately when service starts
- Wait exactly RestartSec after previous run completes
- Continue regardless of exit status
The Timer approach will:
- Run at fixed intervals from service activation
- Allow more sophisticated scheduling (calendar-based, etc.)
- Can be configured to trigger only on successful exits
Implementation Recommendations
For most periodic scripts, timers are preferable because:
- They provide better visibility (systemctl list-timers)
- Allow more flexible scheduling options
- Enable proper dependency handling
- Support calendar-based scheduling when needed
Converting a cron job (e.g., */5 * * * * /path/to/script) to systemd:
# /etc/systemd/system/myscript.service
[Unit]
Description=My Custom Script
[Service]
Type=oneshot
ExecStart=/path/to/script
# /etc/systemd/system/myscript.timer
[Unit]
Description=Run myscript every 5 minutes
[Timer]
OnCalendar=*:0/5
AccuracySec=1m
[Install]
WantedBy=timers.target
For high-frequency tasks (e.g., every minute):
- Timers have slightly less overhead than Restart
- Both approaches avoid cron's forking overhead
- Systemd provides better logging integration
For timer units:
systemctl list-timers --all
journalctl -u myscript.timer -u myscript.service
systemd-analyze verify myscript.*
For restart services:
journalctl -u myscript.service -f
systemctl status myscript.service
systemd-analyze verify myscript.service
For new implementations, prefer systemd timers because they:
- Provide better monitoring capabilities
- Enable more sophisticated scheduling
- Integrate cleanly with systemd's ecosystem
- Maintain clearer separation between execution and scheduling
Only use Restart/RestartSec when you specifically need to:
- Immediately restart failed services
- Maintain exactly-timed intervals between completions
- Handle services that might crash unexpectedly