Главная / База знаний / Cron или systemd timers: что выбрать для отложенных задач
инструкция · 23 октября 2026

Cron или systemd timers: что выбрать для отложенных задач

Сравниваем два способа запускать задачи по расписанию в Linux: классический cron и systemd timers — синтаксис, логирование, persistent-таймеры и практическая миграция с одного на другой.

Два способа запускать задачи по расписанию в Linux

Практически любой сервер рано или поздно обрастает набором задач, которые должны выполняться не по запросу пользователя, а сами по себе через определённые интервалы времени: резервное копирование базы данных ночью, ротация логов раз в неделю, обновление кэша каждые несколько минут, проверка сертификатов на скорое истечение раз в сутки. В мире Linux для этого исторически существует cron — простой и вездесущий планировщик, знакомый почти каждому, кто хоть раз администрировал Unix-подобную систему.

С распространением systemd как основной системы инициализации появился второй, более современный механизм — systemd timers, которые запускают задачи не по отдельному демону cron, а через ту же самую инфраструктуру unit-файлов, что управляет обычными службами. На первый взгляд это выглядит как избыточное усложнение простой задачи «выполнить скрипт по расписанию», но на практике у systemd timers есть несколько практических преимуществ, которые становятся заметны именно на серверах с заметным количеством задач и требований к их надёжности.

Cron не исчез и не устарел — он остаётся отличным выбором для простых, независимых задач; systemd timers решают ту же проблему там, где важны логирование, зависимости между задачами и контроль над пропущенными запусками.

Практический взгляд на сосуществование двух планировщиков в современном Linux

Эта статья разбирает синтаксис обоих механизмов, показывает практическую миграцию задачи с cron на systemd timer, объясняет разницу в логировании и рассказывает, в каких конкретно ситуациях один инструмент оказывается удобнее другого — без универсального совета «используйте только новое» или «незачем менять то, что работает».

Как работает cron: синтаксис и файлы

Cron — это фоновый демон, который раз в минуту читает набор файлов с расписанием (crontab) и запускает те команды, чьё время наступило. Расписание задаётся пятью полями через пробел — минута, час, день месяца, месяц, день недели — за которыми следует сама команда для выполнения.

Примеры записей в crontab: каждые 15 минут, каждый день в 3 ночи, по будням в 9 утра
# минута час день-месяца месяц день-недели команда
*/15 * * * * /usr/local/bin/cache-warmup.sh >> /var/log/cache-warmup.log 2>&1
0 3 * * * /usr/local/bin/backup-db.sh
0 9 * * 1-5 /usr/local/bin/report-daily.sh

Каждый пользователь системы может иметь собственный crontab, редактируемый командой crontab -e, — задачи в нём выполняются от имени этого пользователя без необходимости указывать sudo или дополнительные привилегии внутри самой команды. Помимо персональных crontab, системные задачи часто кладут в /etc/cron.d/ отдельными файлами (с дополнительным полем — именем пользователя, от которого выполнять команду) или используют готовые каталоги /etc/cron.daily/, /etc/cron.weekly/ и /etc/cron.hourly/, куда достаточно положить исполняемый скрипт без какого-либо описания расписания вовсе — механизм run-parts запускает всё содержимое каталога автоматически по своему собственному внутреннему расписанию.

Синтаксис cron поддерживает диапазоны (1-5), списки через запятую (1,15,30) и шаг через слэш (*/15 — каждые 15 единиц), что покрывает подавляющее большинство практических сценариев без необходимости в более сложных выражениях. Главный минус этого синтаксиса — его нечитаемость для человека, не работающего с ним регулярно: строку 0 9 * * 1-5 не так легко расшифровать с первого взгляда, особенно если задач в crontab накопилось несколько десятков за годы эксплуатации сервера.

Как устроены systemd timers: связка service и timer

Systemd timer состоит из двух отдельных unit-файлов, работающих в паре: файл с расширением .service описывает саму команду для выполнения (что именно запускать), а файл с расширением .timer описывает расписание (когда именно это запускать) и ссылается на соответствующий service по имени.

Service-юнит, описывающий саму задачу резервного копирования
# /etc/systemd/system/backup-db.service
[Unit]
Description=Резервное копирование базы данных

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-db.sh
Timer-юнит с расписанием, ссылающийся на service того же имени
# /etc/systemd/system/backup-db.timer
[Unit]
Description=Запуск backup-db.service каждую ночь в 3:00

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

Тип Type=oneshot в service-юните сообщает systemd, что процесс завершится сам после выполнения работы, а не будет работать постоянно, как обычная фоновая служба, — это стандартный выбор для задач по расписанию. Включение таймера выполняется командой systemctl enable --now backup-db.timer, после чего systemd берёт на себя весь дальнейший запуск задачи точно по заданному расписанию, а сам service-юнит отдельно запускать не нужно.

Такое разделение на два файла может показаться избыточным для одной простой команды, но оно даёт практическое преимущество: service-юнит можно запустить вручную в любой момент командой systemctl start backup-db.service для проверки без ожидания расписания, а сама задача получает полный доступ ко всем возможностям systemd — ограничение ресурсов, зависимости от других служб, автоматический перезапуск при сбое и единообразное логирование через journald.

Календарные выражения OnCalendar: гибкость расписания

Директива OnCalendar в timer-юните использует собственный, куда более читаемый синтаксис по сравнению с пятью полями cron. Общий формат — день-недели год-месяц-день час:минута:секунда, при этом любую часть можно заменить звёздочкой, если она не важна для конкретного расписания.

Примеры выражений OnCalendar: ежедневно в 3:00, по будням в 9:00, еженедельно, первого числа каждого месяца, каждые 15 минут
OnCalendar=*-*-* 03:00:00
OnCalendar=Mon..Fri 09:00
OnCalendar=weekly
OnCalendar=*-*-01 00:00:00
OnCalendar=*:0/15

Кроме числовых диапазонов, OnCalendar понимает именованные сокращения вроде weekly, daily, monthly и hourly, а также поддерживает диапазоны через две точки (Mon..Fri) и шаг через слэш (0/15 — с нуля с шагом 15), что покрывает практически все практические расписания более наглядным образом, чем эквивалентная запись в cron.

Проверка корректности и следующего срабатывания выражения OnCalendar перед применением
systemd-analyze calendar "Mon..Fri 09:00"

Команда systemd-analyze calendar — практический инструмент для проверки правильности выражения перед тем, как класть его в реальный unit-файл: она выводит несколько ближайших моментов срабатывания, что позволяет сразу заметить опечатку или неверно понятую логику диапазона, не дожидаясь реального запуска задачи по ошибочному расписанию.

Миграция задачи с cron на systemd timer: практический пример

Разберём конкретный перенос на примере задачи очистки временных файлов, которая раньше запускалась через cron каждый час. Исходная запись в crontab выглядела предельно просто — одна строка без какого-либо контекста о том, что именно делает скрипт и почему выбран именно такой интервал.

Исходная запись в crontab: запуск скрипта очистки каждый час в начале часа
0 * * * * /usr/local/bin/cleanup-tmp.sh

Перенос на systemd timer начинается с создания service-юнита, который просто описывает ту же команду, но уже с человекочитаемым описанием задачи и явным указанием пользователя, от имени которого выполняется команда, — в crontab это определялось положением файла или дополнительным полем, что не всегда очевидно при беглом просмотре.

Два новых unit-файла, полностью заменяющие одну строку из crontab
# /etc/systemd/system/cleanup-tmp.service
[Unit]
Description=Очистка временных файлов приложения

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/cleanup-tmp.sh

# /etc/systemd/system/cleanup-tmp.timer
[Unit]
Description=Запуск очистки временных файлов каждый час

[Timer]
OnCalendar=hourly
Persistent=true

[Install]
WantedBy=timers.target

После создания файлов остаётся выполнить systemctl daemon-reload, чтобы systemd перечитал список unit-файлов, и включить таймер командой systemctl enable --now cleanup-tmp.timer. С этого момента старую строку в crontab можно удалить — оставлять обе версии задачи одновременно означало бы выполнять очистку временных файлов дважды в час, что как минимум неэффективно, а для некоторых сценариев (например, конкурентная запись в один и тот же файл-лог) потенциально проблематично.

Логирование: journald против почты cron

Одно из самых практических различий между двумя механизмами — то, куда попадает вывод выполненной задачи и как быстро администратор узнает о её сбое. Классический cron по умолчанию пытается отправить весь вывод команды (и stdout, и stderr) на локальную почту пользователя, от имени которого выполнялась задача, — на сервере это работает только при настроенном MTA (почтовом агенте вроде Postfix), иначе письмо просто теряется без каких-либо следов ошибки.

Явное указание адреса для писем cron через переменную MAILTO в начале crontab
MAILTO=admin@example.ru
0 3 * * * /usr/local/bin/backup-db.sh

Многие администраторы вместо настройки почты просто перенаправляют вывод скрипта в лог-файл через >> /var/log/имя.log 2>&1, как показано в примерах выше, — это работает, но требует отдельно настраивать ротацию этого лог-файла и вручную проверять его содержимое, поскольку никакого автоматического уведомления о появлении новых строк с ошибкой cron не даёт.

Systemd timers решают эту задачу принципиально иначе: весь stdout и stderr любой задачи, запущенной через service-юнит, автоматически попадает в journald — единый системный журнал, доступный командой journalctl без какой-либо дополнительной настройки самого скрипта или отдельного файла для перенаправления вывода.

Просмотр вывода конкретной задачи за сегодня и последних 50 строк лога
journalctl -u backup-db.service --since today
journalctl -u backup-db.service -n 50 --no-pager

Дополнительное практическое преимущество journald — единообразный формат записей с точными метками времени и кодом завершения процесса, что позволяет быстро понять не только что вывел скрипт, но и завершился ли он успешно (systemctl status backup-db.service покажет последний код возврата), тогда как из письма cron или произвольного лог-файла эту информацию приходится вычленять вручную по содержимому вывода.

Persistent-таймеры и пропущенные запуски

Одна из практических проблем cron — полное отсутствие механизма компенсации пропущенных запусков. Если сервер был выключен или перезагружался ровно в момент, когда должна была сработать ночная задача резервного копирования, эта задача просто не выполнится вовсе, и cron никак не сообщит об этом и не попытается наверстать пропущенный запуск при следующем старте демона.

Директива Persistent=true компенсирует пропущенный из-за выключенного сервера запуск
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

Директива Persistent=true в timer-юните меняет это поведение: systemd запоминает время последнего успешного срабатывания таймера (сохраняя его на диск), и если при следующем старте системы окажется, что запланированный момент запуска уже прошёл, задача будет выполнена почти сразу после загрузки, а не будет ждать следующего по расписанию момента.

Это особенно важно для серверов и рабочих станций, которые не работают круглосуточно, — например, для тестового окружения, выключаемого на ночь, или для ноутбука разработчика. Для production-серверов, работающих постоянно, разница менее заметна на практике, но Persistent=true всё равно остаётся разумной страховкой на случай незапланированной перезагрузки во время обновления системы или аварийного сбоя питания.

Управление и мониторинг: systemctl list-timers

Systemd предоставляет встроенную команду для просмотра состояния сразу всех активных таймеров в системе — с указанием, когда каждый из них сработает в следующий раз и когда сработал в последний.

Список всех таймеров с колонками NEXT (следующий запуск) и LAST (последний запуск)
systemctl list-timers --all

Колонки NEXT и LAST дают мгновенное представление о состоянии всех запланированных задач на сервере без необходимости заглядывать в отдельные конфигурационные файлы каждой из них — если LAST показывает время значительно раньше ожидаемого интервала, это сигнал, что таймер по какой-то причине перестал срабатывать (например, был случайно отключён командой systemctl disable) и требует внимания.

У cron прямого эквивалента этой команде нет: чтобы получить аналогичную картину по всем задачам сервера, пришлось бы вручную просматривать содержимое всех crontab (включая /etc/crontab, /etc/cron.d/ и персональные crontab каждого пользователя) и сопоставлять расписание с текущим временем самостоятельно, что заметно менее удобно при сколько-нибудь большом числе задач.

Когда выбрать cron, а когда systemd timer

Cron остаётся разумным выбором для простых, независимых задач без особых требований к логированию и мониторингу — одноразовый скрипт ротации файлов, унаследованная задача, перенесённая с другого сервера без изменений, или окружение, где важна максимальная переносимость между разными дистрибутивами и даже Unix-подобными системами за пределами Linux, включая старые версии без systemd вовсе.

Systemd timers оправданы там, где важен хотя бы один из следующих факторов: надёжное логирование без дополнительной настройки MTA или ручного перенаправления вывода, необходимость ограничить ресурсы задачи через cgroups (директивы MemoryMax, CPUQuota прямо в service-юните), зависимость от состояния других служб (задача не должна стартовать, пока не поднялась база данных), или потребность в компенсации пропущенных из-за выключения сервера запусков через Persistent=true.

Выбор между cron и systemd timers — это не вопрос «что современнее», а вопрос конкретных требований к конкретной задаче: для простого скрипта раз в день разница может быть непринципиальной, а для критичной задачи резервного копирования возможности systemd timer по логированию и мониторингу становятся практически весомым аргументом.

Практический подход к выбору инструмента планирования

На практике многие серверы используют оба механизма параллельно без каких-либо проблем: cron — для простых унаследованных задач и быстрых разовых скриптов, systemd timers — для новых задач, где важны наблюдаемость и надёжность, особенно на серверах, где резервное копирование или синхронизация данных критичны для бизнеса.

Типичные ошибки при работе с обоими планировщиками

Забытый абсолютный путь к интерпретатору или самому скрипту в crontab — задачи cron запускаются с минимальным набором переменных окружения, без PATH, унаследованного от интерактивной сессии администратора, и относительный путь или голое имя команды, прекрасно работающее в терминале, может просто не найтись при запуске из cron.

Отсутствие MAILTO и MTA одновременно с отсутствием перенаправления вывода в файл — в такой конфигурации сбой задачи cron проходит абсолютно незамеченным, поскольку вывод об ошибке не попадает ни в почту, ни в какой-либо доступный для просмотра лог.

Забытый systemctl daemon-reload после создания или изменения unit-файлов systemd timer — без этой команды systemd продолжает работать со старой версией конфигурации из памяти, и изменения в файле на диске эффекта не дают, что нередко принимают за ошибку в самом синтаксисе.

Включение только timer-юнита без предварительной проверки самого service-юнита вручную командой systemctl start — если в описании команды в ExecStart есть опечатка или неверный путь, об этом узнают только в момент первого срабатывания по расписанию, зачастую ночью, а не сразу при настройке.

Использование WantedBy=multi-user.target вместо WantedBy=timers.target в секции Install timer-юнита — это распространённая опечатка при копировании структуры из обычного service-юнита, из-за которой таймер включается, но не активируется правильно при загрузке системы.

Итог: два инструмента для одной задачи, разные сильные стороны

Cron и systemd timers решают одну и ту же базовую задачу — запуск команды по расписанию, — но делают это с разным набором компромиссов. Cron выигрывает простотой одной строки и универсальностью, systemd timers — встроенным логированием через journald, персистентностью пропущенных запусков и интеграцией с остальной инфраструктурой systemd вроде зависимостей и ограничения ресурсов.

Для нового проекта на современном дистрибутиве с systemd разумно по умолчанию рассматривать именно timers, особенно для задач, критичных для бизнеса вроде резервного копирования, — не потому что cron стал хуже, а потому что дополнительные несколько минут на создание двух unit-файлов вместо одной строки окупаются заметно более прозрачной диагностикой при первом же сбое. Если сомневаетесь, какой вариант лучше подойдёт под конкретную инфраструктуру вашего проекта, опишите задачу в заявке внизу страницы — поможем настроить надёжное расписание задач под ваш сервер.

Переносить существующие задачи из cron в systemd timer целиком и сразу не обязательно — оба механизма прекрасно работают параллельно на одном сервере, и миграцию разумно проводить постепенно, начиная с самых критичных задач, где недостатки cron в логировании и мониторинге ощущаются сильнее всего на практике.

почему мы

Почему стоит выбрать нас

Быстрый запуск

Подберём конфигурацию и развернём проект в день обращения.

Надёжность

NVMe-диски, защита от DDoS и резервные копии по расписанию.

Поддержка 24/7

Дежурные инженеры на связи в любое время суток.

Настройка под ключ

Установим CMS, настроим веб-сервер, SSL и почту за вас.

Перенос без простоя

Аккуратно перенесём сайт и базу данных на новый сервер.

Прозрачные цены

Актуальные тарифы и честный подбор без навязывания.

частые вопросы

Вопросы и ответы

Что проще для новичка — cron или systemd timers?

Cron проще для одной простой задачи: одна строка синтаксиса, знакомая практически всем администраторам Linux независимо от дистрибутива. Systemd timers требуют создать два отдельных файла (service и timer), зато дают заметно больше контроля над самой задачей — от логирования до ограничений по ресурсам.

Работают ли systemd timers на всех дистрибутивах Linux?

Только там, где используется systemd в качестве системы инициализации, — это подавляющее большинство современных дистрибутивов (Ubuntu, Debian, CentOS/RHEL, Fedora), но не все контейнерные образы и не редкие дистрибутивы вроде Alpine с OpenRC или Devuan без systemd, где остаётся только cron.

Куда деваются сообщения об ошибках в cron, если не настроена почта?

Если на сервере не настроен MTA (почтовый агент) для локальной отправки писем, вывод скрипта, который cron пытается отправить на почту, просто теряется без каких-либо следов — это одна из самых частых причин, почему упавшая по cron задача остаётся незамеченной неделями.

Что означает Persistent=true в systemd timer?

Это указание системе выполнить задачу при следующем старте, если запланированный момент запуска был пропущен, например, потому что сервер был выключен или перезагружался в это время, — у обычного cron такого встроенного механизма нет вовсе.

Можно ли ограничить ресурсы, которые может использовать задача по расписанию?

Через systemd timer — да, напрямую в service-юните директивами вроде MemoryMax или CPUQuota, которые применяет cgroups операционной системы; для задачи в cron такое ограничение пришлось бы реализовывать отдельно, например через обёртку systemd-run или утилиту cpulimit.

Как посмотреть, когда задача запускалась в последний раз и когда запустится следующий раз?

Для systemd timer — командой systemctl list-timers, которая покажет колонки NEXT и LAST для каждого активного таймера в системе; для cron такой встроенной команды нет, и придётся смотреть либо системный лог, либо собственный лог-файл самого скрипта.

Можно ли использовать и cron, и systemd timers одновременно на одном сервере?

Да, они никак не конфликтуют друг с другом и работают полностью независимо — многие серверы годами используют оба механизма одновременно: cron для унаследованных задач и простых скриптов, systemd timers для новых задач, где важны логирование и контроль зависимостей.

Нужно ли перезапускать cron или systemd после добавления новой задачи?

Демон cron сам подхватывает изменения в crontab через crontab -e без перезапуска; для systemd timer после создания или изменения unit-файлов нужно выполнить systemctl daemon-reload и затем включить или перезапустить сам таймер командой systemctl enable --now.

Как оплачивать услуги?

Оплата помесячная, для облачных серверов доступна почасовая тарификация. Принимаются карты, банковские переводы и электронные кошельки.

Есть ли круглосуточная поддержка?

Да, дежурные инженеры на связи 24/7.

Выдаёте ли закрывающие документы?

Да, предоставим необходимые документы для оплаты.

Как оставить заявку?

Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.

заявка

Оставьте заявку

Ответим и поможем с подбором и настройкой.