Своя машина с systemd → таймер. Однострочные задачи, контейнеры, чужие ящики → cron.
Cron падает молча — он кладёт письмо в локальный ящик, который никто не читает. Таймеры пишут каждый запуск в журнал: journalctl -u mytimer.service.
Persistent=true догоняет задачи, пропущенные, пока машина была выключена ; cron молча их пропускает.
Одна и та же задача — ночной бэкап в 03:30 — на обоих часах, и то, что каждый оставляет после себя:
CRON · одна строка
30 3 * * * /usr/local/bin/backup.sh
запускается, когда настенные часы совпали. Если упало — вывод уходит письмом в локальный ящик, обычно непрочитанный. Машина была выключена в 03:30? Пропущено, молча.
а он вообще сработал? journalctl -u cron -n 20 или grep CRON /var/log/syslog
ТАЙМЕР SYSTEMD · два файла юнитов
# foo.timer
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
# foo.service — the job itself
юнит первого класса: статус, зависимости, логи на каждый запуск в журнале. Машина спала в нужный час? Persistent=true догонит одним запуском при загрузке.
проверить: systemd-analyze calendar "*-*-* 03:30:00" — следующий запуск, ещё до сохранения
вердикт по графикам: пять полей cron — компактный incumbent; OnCalendar многословнее, но строго выразительнее — Mon..Fri 09..17:00:00 в одну строку, а RandomizedDelaySec не даёт тысяче машин молотить по одному серверу
эти часы пере-стампят по собственным командам записи — они не могут читать журнал вашей машины; где в самом деле ваш журнал: разделы 04 и 05.
Persistent=true догоняет одним запуском при загрузке, если его график был пропущен; cron просто пропускает.network-online.target или точек монтирования; cron заставляет руками писать retry-логику в скрипте.OnBootSec=15min — скачки настенных часов не способны его сбить.Три команды отвечают на всё — показать, что запланировано и когда сработает в следующий раз, прогнать задачу руками ровно как это сделал бы таймер, затем прочитать его логи:
systemctl list-timers --all # every timer, next + last run
sudo systemctl start backup.service # fire the job now, same unit as the timer
journalctl -u backup.service -f # watch its output live
Заметьте разделение: systemctl start backup.timer заряжает график; service — сама задача. Если list-timers показывает ваш таймер, systemctl status backup.service зелёный, а журнал показывает ваш вывод — вся цепочка жива.
verify: systemctl list-timers --all → ваш таймер, прошлый и следующий запуски в списке
Cron-задачи запускаются с облегчённым окружением, поэтому «у меня в shell работает, в cron падает» — целый жанр бага. Чтобы воспроизвести cron верно, гоните команду через sh с окружением, которое использовал бы cron:
crontab -l # confirm the exact line
env -i SHELL=/bin/sh PATH=/usr/bin:/bin sh -c '/usr/local/bin/backup.sh'
grep CRON /var/log/syslog | tail # did cron even fire it? (Debian/Ubuntu)
journalctl -u cron -n 20 # same, on systemd distros
Эта env -i строка — честный ручной тест: голое окружение с cron-дефолтным PATH. Две классики cron — голый PATH и некавыченный % (cron трактует его как перевод строки) — вылазят сразу же в этом тесте.
verify: journalctl -u cron -n 20 → CRON-запись вашей команды
Таймер выглядит мёртвым — обычно это значит, что его service падает мгновенно. Четыре причины, в порядке проверки:
foo.timer зажигает foo.service — опечатка или переименованный юнит, и таймер «запускается» в пустоту. systemctl cat foo.timer показывает точно, в кого целится.sudo systemctl daemon-reload && sudo systemctl restart foo.timer. Без этого ваш новый график не жив.systemctl status foo.timer всегда показывает active, даже когда ничего не делает — читайте list-timers вместо.journalctl -u foo.service --since -1h показывает краш, который list-timers прячет.verify: journalctl -u foo.service --since -1h → краш, который прячет list-timers
| cron | таймер systemd | |
|---|---|---|
| Установка | одна строка crontab | два файла юнитов |
| Логи | локальная почта, обычно не прочитана | журнал, по юниту |
| Пропущенный запуск (машина выключена) | пропущен | одным запуском с Persistent=true |
| Зависимости / порядок | нет — ручками в скрипте | полные зависимости юнитов |
| Случайная задержка | вручную через $RANDOM | RandomizedDelaySec= |
| Тест / dry-run графика | нет | systemd-analyze calendar |
| Везде есть | да — контейнеры, BSD, эмбеддед | нужен systemd (PID 1) |
| Задачи пользователя без root | crontab -e | systemd --user юниты |
Правила на пальцах : доставить задачу на свою машину с systemd → таймер. Быстрое личное напоминание или чужой ящик → cron. Всё, где есть зависимости, повторы или необходимость знать, реально ли отработало → таймер, всегда. Классический паттерн — ночной rsync-бэкап под таймером с Persistent=true — гарантирует, что бэкап случится, даже если машина спала в назначенную минуту, чего cron попросту не умеет.
конец двух часов · ничего не скачано · всё локально