Если вы уже используете systemd для сервисов, логично поручить ему и расписание задач. Таймеры systemd позволяют точнее, чем cron, управлять запуском, видеть состояние и логи в одном месте, добавлять джиттер для разгрузки пиков и «догонять» пропущенные запуски после простоя. В этой статье разберём три ключевых рычага: OnCalendar для календарных расписаний, RandomizedDelaySec для случайной задержки и Persistent для гарантии выполнения после офлайна.
Зачем переходить с cron на systemd timers
- Единая модель: сервис оформлен как unit, таймер — как unit; единое управление через
systemctl, логи — вjournald. - Точность и контроль:
AccuracySecзадаёт окно активации;systemctl list-timersпоказывает ближайшие запуски. - Надёжность:
Persistent=trueзапускает задачу при старте, если она пропущена в офлайне. - Масштабируемость:
RandomizedDelaySecрассеивает одновременные старты на множестве хостов.
На некоторых платформах общего хостинга systemd‑таймеры могут быть недоступны. Для полного контроля над окружением и планировщиком используйте сервер на VDS.
Анатомия: пара .service + .timer
Таймер не делает работу сам — он активирует соответствующий сервис. Принято, чтобы имена совпадали: backup.service и backup.timer. Сервис обычно Type=oneshot, таймер задаёт расписание и параметры.
Пример .service
[Unit]
Description=Nightly backup to object storage
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
User=backup
Group=backup
# Создать каталоги с правильными правами при старте
StateDirectory=backups
WorkingDirectory=/var/lib/backups
# Блокировка от конкурентных запусков
ExecStart=/usr/bin/flock -n /run/backup.lock /usr/local/bin/backup.sh
# Если бэкап длинный — снимите ограничение времени или увеличьте
TimeoutStartSec=0
Nice=10
IOSchedulingClass=best-effort
[Install]
WantedBy=multi-user.target
Пример .timer с OnCalendar, RandomizedDelaySec и Persistent
[Unit]
Description=Nightly backup timer (03:15 local time)
[Timer] 03:15
Persistent=true
RandomizedDelaySec=10m
AccuracySec=1m
Unit=backup.service
[Install]
WantedBy=timers.target
Комментарии к параметрам:
OnCalendar— календарное расписание. Здесь ежедневно в 03:15 по локальному времени.Persistent=true— если хост был выключен в 03:15, задача стартует при включении.RandomizedDelaySec=10m— добавит случайную задержку до 10 минут к моменту срабатывания.AccuracySec=1m— допускает до минуты дрейфа планировщика, снижая тики и экономя ресурсы.
OnCalendar: практические форматы
Календарные выражения в systemd гораздо гибче crontab и читаются как «человеческие» строки. Несколько рабочих паттернов:
- Каждый день в 03:15:
OnCalendar=03:15илиOnCalendar=*-*-* 03:15. - По будням в 09:00:
OnCalendar=Mon..Fri 09:00. - Каждые 15 минут:
OnCalendar=*:0/15илиOnCalendar=*-*-* *:0/15:00. - Первое число месяца в 04:00:
OnCalendar=*-*-01 04:00. - Первые понедельники месяца в 04:00:
OnCalendar=Mon *-*-1..7 04:00. - Ночное окно каждые 5 минут с 01:00 до 03:00:
OnCalendar=*-*-* 01..03:0/5. - Жёстко в UTC:
OnCalendar=Mon..Fri 09:00 UTC.
Совет: проверяйте выражения перед боем. Команда systemd‑analyze calendar "строка" покажет ближайшие срабатывания и быстро выявит ошибки.
Пара нюансов:
- Локальная зона: без явного
UTCрасписание считается в локальном часовом поясе хоста. - Переходы на летнее/зимнее время: одно срабатывание может сдвинуться или повториться при смене TZ. Для строгой предсказуемости используйте
UTC. - Леп‑секунды таймер не моделирует — это нормально для большинства задач.
RandomizedDelaySec: зачем джиттер
Если у вас парк серверов, одновременный старт тяжёлых задач в 03:00 создаёт пики нагрузки на CPU, диск и сеть. RandomizedDelaySec добавляет к каждому срабатыванию непредсказуемую задержку в диапазоне от нуля до указанного значения. Рекомендации:
- Бэкапы, ротация, метрики — 5–20 минут в зависимости от плотности парка.
- Короткие задания раз в 5–15 минут — 30–120 секунд.
- Комбинируйте с
AccuracySec, чтобы диспетчер не пытался «подогнать секунды».
Важно: делайте задания идемпотентными — повторный запуск не должен ломать данные, даже если окна пересеклись.

Persistent: догоняем пропуски после офлайна
Persistent=true делает таймер устойчивым к простоям. Поведение простое: при загрузке systemd проверяет штамп последнего выполнения и расписание; если за время офлайна были срабатывания, задача запустится сразу после старта (с учётом RandomizedDelaySec и AccuracySec).
Детали, о которых стоит знать:
- Штампы лежат в каталоге
/var/lib/systemd/timersв файлах видаstamp-name.timer. - Если пропущено несколько окон, обычно выполняется один «догоняющий» запуск. Если вам нужен реплей «по каждому интервалу», заложите это в логику задания.
- Работает и для монотонных таймеров (
OnBootSec,OnUnitActiveSec): при длительном офлайне после старта будет одна активация.
Точность: AccuracySec и друзья
AccuracySec задаёт максимально допустимое окно неточности активации. Чем оно меньше — тем чаще таймер просыпается и тем выше накладные расходы. Типовые профили:
- Релизные окна, бэкапы ночью —
AccuracySec=1m..5m. - Оперативные задачи раз в 1–5 минут —
AccuracySec=10s..30s. - Критично точный запуск «в секунду» —
AccuracySec=1s, но это редкий кейс.
Практические рецепты
1) Ночной бэкап с джиттером и догонкой
[Timer]
Persistent=true
RandomizedDelaySec=10m
AccuracySec=1m
Unit=backup.service
Такое расписание не сорвётся, если окно выпало на простой, и не создаст пики при множестве серверов. Для детального разбора хранилищ и инструментов посмотрите материал про S3‑бэкапы restic/borg: практика резервного копирования в S3 с restic и borg.
2) Каждые 15 минут, но не синхронно на всех хостах
[Timer]
RandomizedDelaySec=90s
AccuracySec=15s
Unit=housekeeping.service
Подходит для задач вида «собрать метрики, обновить кеши».
3) Будние дни в 09:00 строго по UTC
[Timer] 09:00 UTC
Persistent=true
RandomizedDelaySec=2m
AccuracySec=30s
Unit=reports.service
Актуально для межрегиональных команд и интеграций, где важна унификация времени.
4) Периодическое задание от старта сервиса
[Timer]
RandomizedDelaySec=5m
Persistent=true
Unit=cleanup.service
Так запускают сервисы «каждый час после предыдущего выполнения», стартуя через 5 минут после загрузки. Для баз данных пригодится связка с бэкап‑утилитами, например наш разбор pgBackRest: резервное копирование PostgreSQL через pgBackRest.
Тестирование и эксплуатация
- Загрузить новые юниты:
systemctl daemon-reload. - Включить и запустить таймер:
systemctl enable --now backup.timer. - Посмотреть расписание:
systemctl list-timersилиsystemctl list-timers --all. - Проверить статус:
systemctl status backup.timerиsystemctl status backup.service. - Логи выполнения:
journalctl -u backup.service -S today. - Проверить выражение:
systemd-analyze calendar "Mon..Fri 09:00". - Тест сервиса в обход таймера:
systemctl start --now backup.service.
Если таймер включён, но не срабатывает, проверьте системное время и таймзону, выражение OnCalendar и нет ли условий типа Condition* в юните сервиса.

Типичные ошибки и как их избежать
- Включили сервис, а не таймер. В бою нужно
enableдля.timer, не для.service. - Отсутствуют полные пути в
ExecStart. В среде systemd PATH может отличаться; используйте абсолютные пути. - Параллельные запуски. Помогает
flockили логика блокировки в самом скрипте. - Неправильная зона времени. Для строгих графиков указывайте
UTCвOnCalendar. - Слишком маленький
AccuracySecбез необходимости — повышает нагрузку на таймерный механизм. - Длинные джобы убиваются по таймауту. Установите
TimeoutStartSec=0или разумный лимит. - Запуск от root «по привычке». Лучше задать
User/Group, а также использоватьStateDirectoryиRuntimeDirectory. - Сбои из‑за отсутствия сети при старте. Добавьте
Wants=network-online.targetиAfter=network-online.target, если нужна сеть.
Монотонные и календарные таймеры: когда что
Календарные таймеры (OnCalendar) удобны для «человеческих» графиков: конкретные часы, дни недели, месяцы. Монотонные (OnBootSec, OnUnitActiveSec, OnUnitInactiveSec) подходят для «каждые N времени после события». Несколько ориентиров:
- Ночное окно в 03:00 — календарный.
- Задание «каждый час после предыдущего запуска» — монотонный.
- Нужно не терять задания при офлайне — добавьте
Persistent=trueк любому типу.
Наблюдаемость и обслуживание
- Периодически проверяйте список таймеров:
systemctl list-timers, смотрите поля NEXT и LAST. - Алармы на отсутствие последних запусков: можно парсить
systemctl show -p LastTriggerUSecMonotonic name.timerв скрипте мониторинга. - Версионируйте unit‑файлы в репозитории и деплойте через конфиг‑менеджмент. Это упростит аудит изменений
OnCalendarи параметров точности.
Короткая памятка
OnCalendar— читаемые расписания, проверяйте через systemd‑analyze.RandomizedDelaySec— рассеивает пиковые старты, ставьте в минутах для ночных окон.Persistent=true— гарантии после простоя, «догоняющий» запуск один раз.AccuracySec— баланс точности и накладных расходов.- Делайте задания идемпотентными и защищайте от гонок через
flock. - Тестируйте сервис отдельно от таймера, логи смотрите в
journald.
Подводя итог: связка OnCalendar + RandomizedDelaySec + Persistent закрывает 90% повседневных задач планирования. Остальное — дисциплина в оформлении unit‑файлов, правильные права и наблюдаемость. Настройте один‑два эталонных шаблона таймеров и сервисов, и дальше просто меняйте расписание и команды — управление станет предсказуемым и прозрачным.


