Рабочие заметки

Таймеры systemd вместо cron

Август 2026

Долго не видел смысла уходить с cron: он работает, синтаксис знаком, файл один. Смысл появился в тот момент, когда понадобилось узнать, почему ночная задача не отработала, — и выяснилось, что узнать это неоткуда.

Что реально меняется

Задача становится обычным юнитом. У неё есть статус, есть журнал, есть код возврата, её можно запустить руками той же командой, которой её запускает расписание. В cron всё это заменяется письмом на несуществующий почтовый ящик или строкой в общем логе.

Второе — расписание отделено от задачи. Один и тот же .service можно дёргать и по таймеру, и вручную, и из другого юнита, не дублируя команду в трёх местах.

Четыре директивы, которые важно различать

ДирективаЧто делает
OnBootSecОтсчёт от загрузки системы. Нужен, чтобы не бить в сеть в первую же секунду после старта, пока интерфейсы ещё поднимаются.
OnUnitActiveSecОтсчёт от прошлого запуска. Именно это даёт «раз в сутки», а не «в 3:00».
PersistentЕсли момент запуска был пропущен, потому что машина была выключена, задача выполнится сразу после включения.
RandomizedDelaySecСлучайная задержка. Обязательна, если задача ходит на чужой сервер: иначе все машины с этим конфигом придут туда одновременно.

Про Persistent стоит сказать отдельно. Это ровно та разница, из-за которой на ноутбуке или на машине, которую иногда перезагружают, cron-задача просто не выполняется и никто об этом не узнаёт.

Как это выглядит

[Unit]
Description=Daily configuration refresh

[Timer]
OnBootSec=10m
OnUnitActiveSec=1d
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target

Парный .service — обычный oneshot:

[Unit]
Description=Refresh configuration

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/refresh-config
ProtectSystem=strict
ReadWritePaths=/etc/myapp

Чего не хватает поначалу

Включать нужно таймер, а не сервис:

systemctl enable --now refresh-config.timer

Ошибка на этом месте стоила мне вечера. Сервис при этом показывает inactive (dead), что выглядит как поломка, хотя это нормальное состояние oneshot-задачи между запусками.

Посмотреть расписание целиком:

systemctl list-timers --no-pager

Колонки NEXT и LAST отвечают на главный вопрос эксплуатации — когда задача отработает и отработала ли вообще, — на который cron не отвечает никак.