systemd-timer: планирование вместо cron
cron работает, но его логи — текстовый файл без структуры, а зависимости от сервисов приходится городить через костыли вроде Requires= в shell-скрипте. systemd-timer решает это: единый интерфейс управления, логи в journald, зависимости через familiar After=, WantedBy= — и всё в одном стеке.
Структура .service и .timer
Таймер — это отдельный юнит, который запускает .service. Разделение故意的: сервис можно вызывать и вручную, и по расписанию.
backup.service — обычный юнит, можно запустить через systemctl start backup.service.
backup.timer — триггер. Без него сервис не сработает по расписанию.
Persistent=true — если машина была выключена в момент срабатывания, таймер догонит после загрузки.
Calendar-таймеры (OnCalendar)
Формат OnCalendar= — самый близкий аналог cron-выражений, но с другим синтаксисом.
| Пример | Срабатывает |
|---|---|
OnCalendar=daily | Каждый день в 00:00 |
OnCalendar=*-*-01 03:00 | Первого числа каждого месяца в 03:00 |
OnCalendar=*-*-* 02:00 | Каждый день в 02:00 |
OnCalendar=09..17:00 | Каждый час с 9 до 17 |
OnCalendar=*:0/15 | Каждые 15 минут |
OnCalendar=Mon..Fri 09:30 | Будни в 09:30 |
Можно указать несколько значений через запятую:
Для проверки синтаксиса до применения:
Вывод покажет следующую дату срабатывания. Полезно, когда формулировка «каждый второй вторник» записывается как *-*-1..31 03:00 — проверил, убедился, что не косякнул.
Монотонные таймеры
Монотонные таймеры отсчитывают от события, а не от часов.
| Директива | Срабатывает |
|---|---|
OnBootSec=5min | Через 5 минут после загрузки |
OnStartupSec=10min | Через 10 минут после старта systemd |
OnUnitActiveSec=1h | Через час после последнего запуска сервиса |
OnUnitInactiveSec=1d | Через день после завершения сервиса |
OnBootSec и OnStartupSec похожи, но OnBootSec сбрасывается при каждой загрузке, а OnStartupSec — с момента запуска systemd-менеджера. На практике разница заметна в контейнерах и при live-миграции.
Комбинация OnBootSec + OnUnitActiveSec — способ сделать «каждый час, но не раньше загрузки»:
Проверка: systemctl list-timers
После включения и запуска:
Без --all показывает только активные таймеры. NEXT — когда сработает, LEFT — сколько осталось.
Если таймер не появился в списке — проверить статус:
Частая причина молчащего таймера — забытый WantedBy=timers.target.
Запуск под пользователем (systemd –user)
Не все задачи требуют root. Деплой-скрипты, автоочистка кэша в домашней директории, периодический git fetch — удобнее под пользователем.
Юниты кладутся в ~/.config/systemd/user/:
Активация:
Для пользовательских таймеров нужен linger, если они должны работать без логина:
Типичные ошибки
Missing OnCalendar или монотонный таймер. Без директивы [Timer] таймер не сработает никогда. systemctl start backup.timer запустит юнит, но без расписания он просто лежит.
Забытый WantedBy. Без [Install] юнит не включается при загрузке. systemctl enable backup.timer отработает без ошибок, но в list-timers его не будет.
ExecStart в .timer. Таймер запускает только связанный сервис. Если положить ExecStart в .timer, systemd проигнорирует его и возьмёт из .service.
Persistent без последнего запуска. При первой активации Persistent=true не срабатывает — нет «последнего срабатывания» в истории. Сервис запустится только в следующий расчётный момент.
Логирование: cron vs timer в journald
Cron отправляет вывод в syslog или в cron.log, структура — текстовая строка с timestamp. Для разбора нужен grep или awk.
systemd-timer пишет stdout/stderr сервиса напрямую в journald:
Фильтрация по времени, unit, severity — стандартные флаги journalctl:
| Флаг | Что делает |
|---|---|
-u backup.service | Только этот юнит |
-n 100 | Последние 100 строк |
-f | Follow в реальном времени |
--since "1 hour ago" | За период |
-p err | Только ошибки |
Время срабатывания каждого запуска фиксируется в метаданных. Можно построить историю выполнения без парсинга текстовых логов.
Вывод включает реальное время запуска, что упрощает отладку пропущенных срабатываний.
Итого
Переход с cron на systemd-timer оправдан, если задача уже живёт в systemd-окружении. Единое управление, зависимости через After=, логи в journald — выигрыш ощутимый. Для one-liner в crontab systemd-timer избыточен, но для скриптов с зависимостями, логированием и автозапуском — зрелый инструмент.