Руководство для начинающих по системным таймерам (2026)
Если вы какое-то время пользовались Linux, то наверняка знакомы с заданиями cron. С их помощью можно запускать скрипт каждую ночь в 2 часа или каждые 5 минут. В большинстве современных систем Linux (Debian, Ubuntu, Fedora, Arch, RHEL) в качестве системы инициализации используется systemd, а у systemd есть собственный способ планирования заданий. Мы называем их таймерными модулями.
Таймеры systemd выполняют ту же функцию, что и cron, но они интегрированы в остальную часть systemd. Это означает, что вы получаете правильное логирование, обработку зависимостей и контроль ресурсов бесплатно.
Таймер systemd всегда состоит из двух частей:
.timerunit. Определяет когда что-то должно запуститься..serviceединица измерения. Это определяет, что будет выполняться.
Единственная задача таймера — запустить службу в нужное время. Как только вы разберетесь в этом разделении, все остальное встанет на свои места.
Попробуйте прямо сейчас, прежде чем что-то писать: запустите systemctl list-timers на своем компьютере. Вы почти наверняка увидите, что таймеры уже запущены, такие как apt-daily.timer, logrotate.timer или man-db.timer.
$ systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2026-08-25 11:30:00 UTC 3min 40s Tue 2026-08-25 11:20:00 UTC 6min ago sysstat-collect.timer sysstat-collect.service Tue 2026-08-25 12:21:48 UTC 55min - - fwupd-refresh.timer fwupd-refresh.service Tue 2026-08-25 19:37:38 UTC 8h Tue 2026-08-25 11:06:29 UTC - motd-news.timer motd-news.service Tue 2026-08-25 21:35:55 UTC 10h - - apt-daily.timer apt-daily.service Wed 2026-08-26 00:00:00 UTC 12h - - dpkg-db-backup.timer dpkg-db-backup.service Wed 2026-08-26 00:00:00 UTC 12h - - sysstat-rotate.timer sysstat-rotate.service Wed 2026-08-26 00:07:00 UTC 12h - - sysstat-summary.timer sysstat-summary.service Wed 2026-08-26 00:45:21 UTC 13h - - logrotate.timer logrotate.service Wed 2026-08-26 06:29:14 UTC 19h - - apt-daily-upgrade.timer apt-daily-upgrade.service Wed 2026-08-26 11:06:17 UTC 23h - - man-db.timer man-db.service Wed 2026-08-26 11:12:00 UTC 23h Tue 2026-08-25 11:12:00 UTC 14min ago update-notifier-download.timer update-notifier-download.service Wed 2026-08-26 11:22:00 UTC 23h Tue 2026-08-25 11:22:00 UTC 4min 19s ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Sun 2026-08-30 03:10:01 UTC 4 days - - xfs_scrub_all.timer xfs_scrub_all.service Sun 2026-08-30 03:10:34 UTC 4 days - - e2scrub_all.timer e2scrub_all.service Mon 2026-08-31 00:55:31 UTC 5 days - - fstrim.timer fstrim.service Sun 2026-09-06 01:25:59 UTC 1 week 4 days - - update-notifier-motd.timer update-notifier-motd.service 16 timers listed. Pass --all to see loaded but inactive timers, too.
В них нет ничего особенного. Это обычные таймеры, устроенные так же, как тот, который вы собираетесь собрать сами. Вы можете изучить любой из них с помощью systemctl cat man-db.timer, чтобы понять, как устроен настоящий промышленный таймер.
Обратите внимание, что таймеры зависят от systemd, запущенного с PID 1.
$ ps -p 1 PID TTY TIME CMD 1 ? 00:00:06 systemd
В большинстве контейнеров Docker, некоторых минимальных chroot-окружениях и некоторых конфигурациях WSL systemd вообще не запущен, поэтому таймеры (и systemctl сам systemd) там не будут работать без дополнительной настройки. Если созданный вами таймер не выполняет никаких действий, сначала проверьте ps -p 1 , прежде чем приступать к отладке файлов модулей.
1. Базовая настройка
Допустим, вы хотите создавать резервную копию папки каждый день в 3:30 утра. Вам понадобятся два файла.
- Блок службы
- Блок таймера
Сначала создайте блок службы:
sudo nano /etc/systemd/system/backup.service
Добавьте в него следующие строки:
[Unit] Description=Back up home directory [Service] Type=oneshot ExecStart=/usr/local/bin/backup.sh Nice=19 IOSchedulingClass=idle ProtectSystem=strict ReadWritePaths=/var/backups
Затем создайте таймер:
sudo nano /etc/systemd/system/backup.timer
Добавьте следующие строки:
[Unit] Description=Запускать backup.service ежедневно в 3:30 [Timer] OnCalendar=*-*-* 03:30:00 Persistent=true [Install] WantedBy=timers.target
Обратите внимание на несколько моментов:
- Таймер и служба имеют одно и то же имя (
backup). systemd использует это совпадение имен для их связывания. Указывать таймеру другое имя службы нужно только в том случае, если вы сами задалиUnit=. Type=oneshotподходит для большинства запланированных задач, которые выполняются до конца самостоятельно. Если ваш скрипт запускает фоновый процесс или переходит в режим демона,Type=oneshotне будет корректно отслеживать его выполнение, и вам понадобятсяType=forkingилиRemainAfterExit=true. В большинстве скриптов, которые вы пишете сами, это не понадобится.Nice=19иIOSchedulingClass=idleсообщают ядру, что у этой задачи низкий приоритет, поэтому она не конкурирует с более срочными задачами.ProtectSystem=strictделает всю файловую систему доступной только для чтения для этой службы, за исключением путей, которые вы явно разрешаете с помощьюReadWritePaths=. Все это не является обязательным условием для работы таймера, но это небольшие и недорогие меры предосторожности, которые стоит добавить по умолчанию, когда вы минуете этап «а запустится ли это вообще».- Вы включаете и запускаете таймер, а не службу. Непосредственно запускать службу нужно только для тестирования.
sudo systemctl daemon-reload sudo systemctl enable --now backup.timer
2. Два способа планирования: календарное время и относительное время
Здесь большинство новичков путаются, так что давайте не будем торопиться.
2.1 Календарные таймеры (OnCalendar=)
Они срабатывают в фиксированное время, как и cron. Синтаксис взят из systemd.time(7):
OnCalendar=*-*-* 03:30:00 # каждый день в 3:30:00 утра OnCalendar = Mon,Fri *-*-* 09:00:00 # каждый понедельник и пятницу в 9 утра OnCalendar=*-*-01 00:00:00 # первого числа каждого месяца, в полночь OnCalendar=weekly # сокращенный список для понедельника в 00:00 OnCalendar=daily # сокращенный список для каждого дня в полночь OnCalendar=hourly # сокращенный список для начала каждого часа OnCalendar=*:0/15 # каждые 15 минут
Вот небольшой лайфхак, который значительно упрощает жизнь. Вы можете протестировать любое из этих выражений, не дожидаясь его срабатывания:
$ systemd-analyze calendar "Mon,Fri *-*-* 09:00:00"
Original form: Mon,Fri *-*-* 09:00:00
Normalized form: Mon,Fri *-*-* 09:00:00
Next elapse: Mon 2026-08-17 09:00:00 IST
(in UTC): Mon 2026-08-17 03:30:00 UTC
From now: 1 day 18h left
Запустите это перед включением нового таймера. Это убережет вас от классической ошибки — написания строки OnCalendar, которая выглядит правильно, но срабатывает не вовремя.
Есть и вторая, дополнительная проверка, которую тоже стоит провести:
systemd-analyze verify
Эта команда загружает файл модуля так же, как это делает systemd, и выводит все синтаксические ошибки, ничего не запуская.
sudo systemd-analyze verify /etc/systemd/system/backup.timer
Это важно, потому что systemd часто прощает опечатки в файлах модулей. Неправильно написанная директива может быть проигнорирована без каких-либо уведомлений, и ваш таймер просто не будет работать так, как вы ожидали. Возьмите за правило запускать команду systemd-analyze verify после каждого редактирования, сразу после daemon-reload.
Обратите внимание, что таймеры systemd по умолчанию не являются точными. Существует скрытая AccuracySec= настройка со значением по умолчанию 1 минута, поэтому systemd может сдвинуть время запуска вашего задания на минуту в ту или иную сторону. Это делается намеренно, чтобы объединять близкие по времени пробуждения и экономить электроэнергию (это очень важно для ноутбуков).
Если вам действительно нужна точность до секунды, установите AccuracySec=1us в разделе [Timer]. Почти для всего, что вы будете создавать на начальном этапе, подойдут настройки по умолчанию, и вы можете ими пренебречь.
2.2 Относительные таймеры (OnBootSec=, OnUnitActiveSec= и другие)
Этим не нужны настенные часы. Вместо этого они запускаются через определенное время после какого-либо события. В cron нет ничего подобного, так что стоит хорошо в этом разобраться.
| Директива | Fires… |
|---|---|
OnBootSec=15min | Через 15 минут после загрузки компьютера |
OnStartupSec=15min | Через 15 минут после запуска systemd |
OnUnitActiveSec=1h | Через 1 час после последнего запуска |
OnUnitInactiveSec=1h | этого модуля1 час после последнего остановки |
OnActiveSec=30s | этого модуля30 секунд после активации самого модуля таймера |
Вот ловушка, в которую попадает много людей. OnUnitActiveSec= и OnUnitInactiveSec= звучат почти одинаково, но это не так. Для короткой oneshot задачи разница едва ли имеет значение, поскольку задача в любом случае запускается и останавливается практически мгновенно. Но в случае с долго работающим сервисом эти два метода ведут себя совершенно по-разному. OnUnitActiveSec=1h отсчитывает время с момента запуска сервиса. OnUnitInactiveSec=1h отсчитывает время с момента его остановки. Если перепутать эти методы, ваше расписание незаметно сойдет с курса.
Распространенный шаблон: запускать скрипт очистки через 10 минут после загрузки, а затем еще раз каждые 6 часов.
[Timer] OnBootSec=10min OnUnitActiveSec=6h
3. Восполнение пропущенных запусков с помощью Persistent=
Допустим, ваш ноутбук находится в спящем режиме в 3:30 утра, когда backup.timer должен запуститься. Ничего не происходит. Задание просто пропускается, если только вы не добавите эту строку:
[Timer] Persistent=true
При использовании Persistent=true systemd запоминает, когда таймер срабатывал в последний раз (это значение сохраняется в /var/lib/systemd/timers/). Если ваш компьютер пропустил запланированное выполнение задачи из-за того, что был выключен или находился в режиме сна, задание будет выполнено один раз вскоре после следующей загрузки, чтобы наверстать упущенное. Это во многом похоже на работу старого инструмента anacron . Вот почему вам почти по умолчанию понадобится Persistent=true на ноутбуках и настольных компьютерах, в то время как серверам, которые работают постоянно, это часто не требуется.
4. Распределение нагрузки с помощью RandomizedDelaySec=
Представьте себе: вы управляете пятьюдесятью серверами, и все они запускают OnCalendar=daily в полночь. Все пятьдесят серверов обращаются к вашему серверу резервного копирования в одну и ту же секунду. Это как стадо слонов, которое может все разрушить.
Исправьте это следующим образом:
[Timer] OnCalendar=daily RandomizedDelaySec=1800
Теперь каждая машина выбирает свою собственную случайную задержку до 30 минут, прежде чем запустить таймер. Нагрузка распределяется естественным образом. Если вы хотите, чтобы каждая машина каждый раз выбирала одну и ту же задержку (а не новую случайную при каждом запуске), добавьте FixedRandomDelay=true (доступно начиная с systemd 247).
5. Команды systemd Timers, которые вы будете использовать каждый день
# Перечислите все активные таймеры и время следующего запуска каждого из них systemctl list-timers # Включить таймеры, которые отключены или еще не сработали systemctl list-timers --all # Проверить состояние определенного таймера systemctl status backup.timer # Проверьте, как прошел последний запуск службы systemctl status backup.service # Прочитайте журналы службы, в которой сработал таймер journalctl -u backup.service # Запустите службу прямо сейчас, не дожидаясь срабатывания таймера sudo systemctl start backup.service # Включите при загрузке и запустите немедленно sudo systemctl enable --now backup.timer # Остановите и отключите его sudo systemctl disable --now backup.timer # Запустите это после редактирования любого модульного файла sudo systemctl daemon-reload
systemctl list-timers дает вам краткое описание в одной строке:
NEXT LEFT LAST PASSED UNIT ACTIVATES Mon 2026-08-17 03:30:00 IST 12h left Sun 2026-08-16 03:30:00 IST 9h ago backup.timer backup.service
Вы сразу видите, когда он запустится в следующий раз, сколько времени осталось, когда он запускался в последний раз и какую службу он запускает. При использовании cron вам, как правило, приходится полагаться на crontab -l и надеяться на лучшее. Это действительно шаг вперед.
Одно небольшое замечание по поводу логов: journalctl -u backup.service предполагает, что журнал действительно ведет историю. Некоторые минимальные или встроенные дистрибутивы поставляются с Storage=volatile в /etc/systemd/journald.conf, а это значит, что логи хранятся только в памяти и исчезают при перезагрузке. Если вам кажется, что логи исчезли, в первую очередь проверьте этот параметр.
6. Что происходит, если задание все еще выполняется, когда таймер срабатывает повторно
Допустим, backup.service обычно завершается за 5 минут, но сегодня по какой-то причине оно все еще выполняется спустя 6 часов, когда таймер должен сработать повторно. Что происходит?
Ничего страшного, но, возможно, не совсем того, чего вы ожидали: systemd не запускает второй экземпляр, который будет работать параллельно. Активация уже активного модуля фактически ничего не меняет. Следующий запланированный запуск таймера проходит без изменений, и сервис продолжает работать до завершения.
Для большинства задач это именно то, что вам нужно, поскольку она предотвращает неконтролируемое нагромождение задач. Но это также означает, что если задание зависнет, вы можете потерять целый день работы, и нигде не будет отображаться ошибка.
Два практических совета:
- Если задача действительно не должна выполняться параллельно и не должна зависать без каких-либо признаков, используйте
TimeoutStartSec=в[Service](см. список подводных камней ниже), чтобы зависшее задание прерывалось, а не блокировало все последующие. - Если вы подозреваете, что работа застопорилась,
systemctl status backup.serviceпокажет, что она выполняетсяactive (running)гораздо дольше, чем ожидалось. Это ваш сигнал.
7. Быстрые одноразовые таймеры без написания файлов модулей
Иногда нужно запустить что-то один раз, как можно скорее, не создавая пару .timer и .service . systemd-runКоманда выполняет это на лету:
# Запустите команду через 10 минут
sudo systemd-run --on-active="10min" /path/to/script.sh
# Запуск в определенную дату и время
sudo systemd-run --on-calendar="2026-08-20 22:00:00" /path/to/script.sh
# Запускайте через 5 минут после каждой загрузки от имени вашего собственного пользователя
systemd-run --user --on-boot="5min" /path/to/script.sh
systemd-run создает временный таймер и службу в фоновом режиме (их можно увидеть с помощью systemctl list-timers), которые автоматически удаляются при перезагрузке.
Это отличный вариант для тестирования идеи расписания или для задачи, которая нужна вам только один раз. Если вы хотите, чтобы что-то пережило перезагрузку и работало вечно, вернитесь к написанию полноценных файлов модулей, как показано выше.
8. Запуск таймеров без прав суперпользователя
До сих пор мы рассматривали общесистемные модули в /etc/systemd/system/. Вы также можете настроить таймеры от имени обычного пользователя с помощью флага --user. root не требуется!
mkdir -p ~/.config/systemd/user # поместите backup.service и backup.timer в эту папку systemctl --user daemon-reload systemctl --user enable --now backup.timer
Пожалуйста, обратите внимание, что пользовательские таймеры обычно работают только во время активного сеанса. Если вы хотите, чтобы они продолжали работать даже после выхода из системы, включите функцию задержки (для этого нужны права суперпользователя, даже для вашей собственной учетной записи):
sudo loginctl enable-linger $USER
9. Таймеры systemd в сравнении с cron: когда что использовать
Преимущества таймеров:
они обеспечивают настоящий логинг через journalctl, так что вам больше не нужны MAILTO= хаки или ручное перенаправление вывода. Вы также можете указать правильные зависимости, например After=network-online.target или Requires=postgresql.service, чего просто не может сделать cron.
Вы можете изолировать службу с помощью таких параметров, как ProtectSystem=strict или PrivateTmp=true. Кроме того, вы получаете встроенное тестирование (systemd-analyze calendar) и наглядный обзор (list-timers), а также упреждающее поведение и джиттер, которые реализованы изначально, а не с помощью скриптов.
Аргументы в пользу cron:
Он работает практически везде, включая системы без systemd, такие как Alpine с OpenRC, BSD или урезанная версия контейнера. Однострочная crontab запись намного проще, чем написание двух юнит-файлов, особенно для задачи, у которой вообще нет зависимостей.
И если ваша команда уже много лет использует инструменты на основе cron, то переход на новый способ может не стоить затраченных усилий.
Так что же выбрать?
В системах на базе systemd таймеры обычно являются оптимальным решением по умолчанию, особенно если для вас важны логирование, восстановление после пропущенных запусков или порядок выполнения зависимостей.
Для быстрого, портативного скрипта, не требующего зависимостей, cron по-прежнему отлично подходит. Считайте, что это не столько правило, сколько решение, принимаемое в зависимости от вашей среды.
9.1 Преобразование строки crontab в OnCalendar
Если вы переносите существующие задания cron, то будете использовать это сопоставление снова и снова. Поля cron: minute hour day month weekday; OnCalendar= меняет порядок на weekday year-month-day hour:minute:second.
| cron | Значение | OnCalendar= |
|---|---|---|
0 3 * * * | Каждый день в 3:00 | OnCalendar=*-*-* 03:00:00 |
*/15 * * * * | Каждые 15 минут | OnCalendar=*:0/15 |
0 9 * * 1-5 | В будние дни в 9:00 | OnCalendar=Mon..Fri 09:00:00 |
0 0 1 * * | В первое число каждого месяца | OnCalendar=*-*-01 00:00:00 |
0 0 * * 0 | Каждое воскресенье в полночь | OnCalendar=Sun *-*-* 00:00:00 |
@reboot | При загрузке | OnBootSec=0 (эквивалента в OnCalendar нет — это монотонный таймер) |
Обратите внимание на последнюю строку: @reboot не имеет календарного эквивалента, потому что вообще не привязано ко времени на настенных часах. Именно для этого и существует OnBootSec= — хороший пример того, как таймеры systemd делают то, что структурно невозможно в cron.
10. Оповещение о сбое задания
Этот вопрос почти все задают сразу после того, как их первый таймер начинает работать: «Как узнать, что он перестал работать?»
В случае с cron честный ответ обычно звучал так: «Никак, пока кто-нибудь не заметит, что резервные копии пропали».
У systemd есть реальный ответ: OnFailure=.
Добавьте его в свой сервис, указав на второй, небольшой сервис, который отправляет уведомления:
# /etc/systemd/system/backup.service [Unit] Description=Back up home directory OnFailure=alert-on-failure@%n.service [Service] Type=oneshot ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/alert-on-failure@.service [Unit] Description=Send a failure alert for %i [Service] Type=oneshot ExecStart=/usr/local/bin/notify-failure.sh "%i"
%n расширяется до имени устройства, вышедшего из строя, и %i служба оповещения получает его. notify-failure.sh может быть таким же простым, как curl вызов вебхука или вызов mail. Дело не в механизме уведомления, а в том, что OnFailure= срабатывает автоматически, только в случае сбоя, без опрашивания и без дополнительного таймера для проверки первого срабатывания.
11. Полный пример: Еженедельная очистка журнала
# /etc/systemd/system/logcleanup.service [Unit] Description=Purge logs older than 30 days After=network.target [Service] Type=oneshot ExecStart=/usr/bin/find /var/log/myapp -name "*.log" -mtime +30 -delete
# /etc/systemd/system/logcleanup.timer [Unit] Description=Weekly log cleanup [Timer] OnCalendar=Sun *-*-* 04:00:00 Persistent=true RandomizedDelaySec=600 [Install] WantedBy=timers.target
Теперь запускаем его и проверяем, что он действительно запланирован:
sudo systemctl daemon-reload sudo systemctl enable --now logcleanup.timer systemd-analyze calendar "Sun *-*-* 04:00:00" # подтверждаем правильность расписания systemctl list-timers logcleanup.timer # подтверждаем, что systemd знает о нем sudo systemctl start logcleanup.service # сразу же проверяем его работу journalctl -u logcleanup.service -n 20 # проверяем, что все сработало
12. Ошибки, которые совершают новички (и как их избежать)
- Пропуск
daemon-reload. После редактирования файла unit в памяти systemd остается старая версия, кэшированная в памяти. Всегда перезагружайте. - Включение службы вместо таймера. При этом задание выполняется один раз, немедленно, и больше никогда не выполняется по расписанию.
- Перепутывание
OnUnitActiveSec=иOnUnitInactiveSec=. В основном это связано с длительной работой сервисов, как описано в разделе 3.2. - Не учитываем
Persistent=true. На ноутбуке это означает, что ваша ежедневная работа незаметно пропускает дни, когда машина переходит в режим ожидания в запланированное время. - Забываем о
[Install]разделе. БезWantedBy=timers.targetподключатьsystemctl enableне к чему, поэтому таймер не запустится при загрузке. - Не проверяет состояние после того, как что-то пошло не так. Неудачное
oneshotзадание отображается какfailed, и таймер все равно сработает снова в следующем цикле. Но вы не заметите, пока не проверите журналы или не настроитеOnFailure=оповещение. - Ожидая
OnCalendar=выстрела в ту же секунду. Как описано в разделе 3.1, значение по умолчаниюAccuracySec=1minозначает, что ваша работа может затянуться до минуты. Это нормально, а не ошибка. - Доверять файлу unit только потому, что
daemon-reloadне жаловался. systemd может молча игнорировать директиву с ошибкой, а не выдавать ошибку. Запускайтеsystemd-analyze verifyпосле внесения изменений, чтобы уловить это. - Предполагая, что зависшее задание в конечном итоге завершится само по себе. По умолчанию этого не происходит. Если выполнение скрипта может зависнуть (из-за устаревшего сетевого подключения или ожидания ввода, который так и не поступил), добавьте
TimeoutStartSec=в[Service], чтобы systemd завершила его, а не блокировала при каждом последующем запуске, как описано в разделе 7.
13. Краткое руководство по таймерам Systemd
| Задача | Команда |
|---|---|
| Список всех запланированных таймеров | systemctl list-timers --all |
| Проверка календарного выражения | systemd-analyze calendar "EXPR" |
| Проверка файла модуля на наличие синтаксических ошибок | sudo systemd-analyze verify /path/to/name.timer |
| Просмотр активной конфигурации файла модуля | systemctl cat name.timer |
| Включить и запустить таймер | sudo systemctl enable --now name.timer |
| Запустить службу прямо сейчас | sudo systemctl start name.service |
| Проверить журналы службы | journalctl -u name.service |
| Перезагрузить после редактирования модулей | sudo systemctl daemon-reload |
| Отключить таймер | sudo systemctl disable --now name.timer |
| Выполнить разовую команду в ближайшее время, без юнит-файла | systemd-run --on-active="10min" /path/to/script.sh |
Подробнее о systemd-таймерах читайте на страницах руководства.
man systemd.timerполный список директивman systemd.timeсинтаксис календаря и временных интерваловman systemd.serviceдля параметров службы, таких какType=и директив «песочницы»
Редактор: AndreyEx