OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

systemd‑таймеры на практике: OnCalendar, RandomizedDelaySec и Persistent

Пособие для админов и девопсов по systemd‑таймерам: как заменить cron, грамотно задать OnCalendar, добавить джиттер через RandomizedDelaySec и не терять окна с Persistent. Разберём форматы, точность, отладку и безопасные unit‑шаблоны.
systemd‑таймеры на практике: OnCalendar, RandomizedDelaySec и Persistent

Если вы уже используете 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 — допускает до минуты дрейфа планировщика, снижая тики и экономя ресурсы.
FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

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, чтобы диспетчер не пытался «подогнать секунды».
Важно: делайте задания идемпотентными — повторный запуск не должен ломать данные, даже если окна пересеклись.

Вывод systemd list-timers в терминале: ближайшие и последние срабатывания

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* в юните сервиса.

Проверка unit-файлов и журналов systemd при настройке таймеров

Типичные ошибки и как их избежать

  • Включили сервис, а не таймер. В бою нужно 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‑файлов, правильные права и наблюдаемость. Настройте один‑два эталонных шаблона таймеров и сервисов, и дальше просто меняйте расписание и команды — управление станет предсказуемым и прозрачным.

Поделиться статьей

Вам будет интересно

cron healthchecks на VDS: контроль фоновых задач и защита от дабл-старта OpenAI Статья написана AI (GPT 5)

cron healthchecks на VDS: контроль фоновых задач и защита от дабл-старта

Регулярные задачи на VDS часто живут своей жизнью: падают молча, зависают, стартуют в двух экземплярах и конфликтуют за ресурсы. Р ...
HTTP end-to-end tracing: X-Request-ID, W3C Trace Context и заголовки OpenTelemetry OpenAI Статья написана AI (GPT 5)

HTTP end-to-end tracing: X-Request-ID, W3C Trace Context и заголовки OpenTelemetry

Когда микросервисов становится десяток и больше, а запросы проходят через несколько gateway, очередей и фоновых воркеров, простого ...
S3 и CDN для WordPress и Laravel: offload медиа и статики без боли OpenAI Статья написана AI (GPT 5)

S3 и CDN для WordPress и Laravel: offload медиа и статики без боли

Разбираем, как вынести медиа и статические файлы WordPress и Laravel в S3‑совместимый object storage и повесить сверху CDN. Пошаго ...