Таймери systemd — вбудований у systemd механізм запуску задач за розкладом, альтернатива cron на дистрибутивах, де systemd вже керує службами. Cron нікуди не зник і продовжує працювати, але в таймерів є можливості, яких у cron немає: журналювання через journalctl, явні залежності між задачами, повтор під час збою. Розбираємо, коли перехід на таймери дає реальну зручність, а коли cron простіше лишити як є.
Чим таймер systemd відрізняється від cron
Cron зберігає розклад в одному рядку текстового файлу і запускає команду у вказаний час, не знаючи нічого про те, що відбувалося до і після. Таймер systemd складається з двох юнітів: .timer задає розклад, а .service описує саму задачу так само, як для будь-якої іншої служби — із залежностями, змінними середовища і користувачем, від імені якого вона виконується.
Це розділення означає, що одну й ту саму задачу можна запустити вручну через systemctl, подивитися її статус і останній результат — без потреби шукати запис у пошті root або в окремому лозі, який сама задача мала завести самостійно.
Коли таймер зручніший за cron
Перевага таймерів найпомітніша в кількох конкретних ситуаціях:
- Потрібно бачити історію запусків задачі і код завершення без налаштування окремого журналювання — усе вже в journalctl.
- Задача має запуститися лише після успішного старту іншої служби, наприклад після готовності бази даних.
- Потрібен повтор під час збою: таймер можна налаштувати на перезапуск сервісу через заданий інтервал після помилки.
- Кілька задач конкурують за ті самі ресурси, і потрібно явно задати, що одна не запуститься, доки не завершиться інша.
- Розклад має переживати пропущений запуск через вимкнений сервер — опція Persistent запускає задачу під час наступного завантаження.
Як створити таймер і сервіс
Мінімальний комплект — два файли юнітів з однаковою назвою і різними розширеннями: .service із самою командою і .timer із розкладом, після чого вмикається саме таймер, а не сервіс безпосередньо.
# Увімкнути і запустити таймер backup.timer
systemctl enable --now backup.timer
# Перевірити статус і час наступного запуску
systemctl list-timers backup.timer
Логи і журнал таймера
Результат кожного запуску таймера потрапляє в journalctl під назвою .service, а не розсилається поштою, як у cron. Це зручно для читання в моменті, але у журналу systemd є свій лімiт обсягу на диску, який із часом заповнюється записами всіх юнітів, не лише таймерів.
Якщо задача сама пише детальний лог в окремий файл — наприклад, вивід бекапу або імпорту, — для цього файлу, а не для журналу systemd, потрібна окрема ротація, налаштована так само, як для будь-якого іншого зростаючого лога: це розібрано в статті про налаштування logrotate.
Приклади застосування: PHP-задачі і WordPress
Таймери однаково добре підходять і під власні PHP-скрипти, що запускаються з командного рядка, і під заміну вбудованого WP-Cron у WordPress на системний розклад — в обох випадках задача описується явною командою з конкретним шляхом до php-бінарю, а не викликом через веб-сервер під час кожного візиту на сайт.
Різниця між запуском PHP-скрипту з CLI і з-під FPM — у наборі змінних середовища і в правах користувача, під яким іде виконання, і це важливо врахувати під час перенесення задачі в сервіс-юніт; подробиці — у статті про PHP CLI і FPM у cron. Готовий приклад перенесення самого WP-Cron із запуску за візитами на системний розклад є в статті про системний cron для WP-Cron.
| Параметр | cron | Таймер systemd |
|---|---|---|
| Зберігання розкладу | рядок у crontab | окремий юніт .timer |
| Журнал запусків | пошта root або свій лог | journalctl без додаткового налаштування |
| Залежності між задачами | не підтримуються | через параметри юніта |
| Повтор під час збою | не підтримується | налаштовується в .service |
Часті питання
Чи потрібно видаляти старі завдання cron під час переходу на таймери?
Так, одну й ту саму задачу не варто тримати одночасно в crontab і в юніті таймера — вона виконається двічі і може призвести до конфлікту, наприклад під час одночасного запису в один файл. Переносити задачі варто по одній, перевіряючи кожну перед видаленням із cron.
Чи можна використовувати таймери в контейнері Docker?
Усередині звичайного контейнера systemd як система інiціалiзації, як правило, не запущений, тому таймери там не працюють так само, як на звичайному сервері. Задачу за розкладом для контейнера логічніше запускати таймером systemd на хості, який звертається до контейнера зовні, а не намагатися завести systemd усередині самого контейнера.
Що станеться, якщо сервер був вимкнений у момент запланованого запуску?
За замовчуванням такий запуск просто зникне, як і в cron. Якщо потрібно гарантувати виконання задачі хоча б раз за період, у юніті .timer вмикається опція Persistent — тоді пропущений запуск виконається одразу після наступного завантаження системи.