Cron: задачи по расписанию в Linux
Разбираем синтаксис cron, показываем практические примеры автоматизации бэкапов и обслуживания сервера, а также типичные ошибки и способы мониторинга выполнения задач.
Зачем нужны задачи по расписанию
Многие рутинные операции на сервере должны выполняться регулярно без участия человека: резервное копирование базы данных каждую ночь, ротация и очистка старых логов, обновление кэша, отправка email-дайджестов, проверка сертификатов на скорое истечение. Cron — стандартный планировщик задач в Unix-подобных системах, который выполняет заданные команды по расписанию.
Cron работает как фоновая служба (демон), которая каждую минуту проверяет список запланированных задач и запускает те, время выполнения которых наступило. Расписание описывается компактной строкой из пяти полей, задающих минуту, час, день месяца, месяц и день недели.
Cron — один из старейших и при этом до сих пор надёжно работающих компонентов Unix-систем: простой, предсказуемый и не требующий сложной инфраструктуры для решения задачи «выполнить команду в такое-то время».
Общая характеристика cron в документации по системному администрированию Linux
Эта статья разбирает синтаксис crontab, даёт практические примеры для типичных серверных задач, показывает частые ошибки новичков и способы убедиться, что запланированная задача действительно выполняется, а не молча падает с ошибкой уже несколько недель.
Синтаксис crontab: пять полей расписания
Каждая строка crontab состоит из пяти полей расписания и команды для выполнения. Поля идут в строгом порядке: минута (0-59), час (0-23), день месяца (1-31), месяц (1-12) и день недели (0-6, где 0 — воскресенье).
# минута час день_месяца месяц день_недели команда
30 3 * * * /usr/bin/php /var/www/example.ru/cron/backup.php
Звёздочка (*) в поле означает «любое значение» — в примере выше день месяца, месяц и день недели заданы звёздочками, что означает выполнение каждый день независимо от даты. Символ /N задаёт шаг — например, */15 в поле минут означает «каждые 15 минут».
*/15 * * * * /usr/bin/php /var/www/example.ru/cron/check-queue.php
0 */4 * * * /usr/bin/php /var/www/example.ru/cron/sync-stock.php
Через запятую можно перечислить несколько конкретных значений, а через дефис — диапазон: например, 0 9-18 * * 1-5 означает «в начале каждого часа с 9 до 18 по будням» — удобно для задач, которые должны выполняться только в рабочее время.
Редактирование crontab
Личный crontab текущего пользователя редактируется командой crontab -e, которая открывает файл расписания в текстовом редакторе по умолчанию (обычно nano или vi). Просмотреть текущие задачи без редактирования можно командой crontab -l.
crontab -e
crontab -l
crontab -r
Команда crontab -r удаляет весь crontab пользователя без подтверждения — стоит быть внимательным с этой командой, особенно если расписание не сохранено где-либо ещё, например в системе контроля версий инфраструктурных скриптов.
Для задач, требующих прав root (например, обновление системных пакетов), редактируется crontab пользователя root командой sudo crontab -e — важно не путать личный crontab обычного пользователя с crontab root, так как задачи в них выполняются с разными правами доступа.
Практические примеры для типичного сервера
Ежедневный бэкап базы данных с ротацией старых копий — одна из самых частых задач cron на любом сервере с сайтом. Комбинация mysqldump и последующего удаления файлов старше определённого срока хранения умещается в одну строку расписания.
0 2 * * * /usr/bin/mysqldump -u backup_user -p"пароль" myapp | gzip > /backups/myapp_$(date +\%Y\%m\%d).sql.gz
30 2 * * * find /backups -name "*.sql.gz" -mtime +14 -delete
Обратите внимание на экранирование знака процента (\%) в первой строке — в crontab символ % имеет особое значение (перевод строки внутри команды), и его нужно экранировать обратным слэшем, если он используется как часть команды, например в формате даты.
0 4 * * 0 systemctl restart php8.3-fpm
0 0 1 * * /usr/bin/certbot renew --quiet
Периодический перезапуск PHP-FPM (или любого долго работающего процесса) раз в неделю — практика, которая помогает бороться с постепенным ростом потребления памяти (memory leak) в приложениях, где такая утечка полностью не устранена на уровне кода, хотя правильным решением остаётся исправление самой утечки, а не регулярный перезапуск как обходной путь.
Специальные строки-сокращения
Для самых частых интервалов cron поддерживает читаемые сокращения вместо пяти полей — они делают расписание нагляднее для типичных случаев вроде «раз в день» или «раз в час».
@reboot /usr/local/bin/startup-check.sh
@daily /usr/bin/php /var/www/example.ru/cron/daily-report.php
@hourly /usr/bin/php /var/www/example.ru/cron/check-health.php
@weekly /usr/bin/certbot renew --quiet
@reboot заслуживает отдельного внимания — эта задача выполняется один раз при каждой загрузке (или перезагрузке) сервера, что удобно для скриптов проверки состояния после рестарта или восстановления временных файлов, которые не должны переживать перезагрузку.
Логирование вывода и уведомления об ошибках
По умолчанию весь вывод команды (и стандартный вывод, и ошибки), если он не перенаправлен явно, cron пытается отправить на локальную системную почту пользователя — на большинстве серверов локальная почта не настроена на пересылку куда-либо, и такие сообщения просто накапливаются непрочитанными.
0 3 * * * /usr/bin/php /var/www/example.ru/cron/backup.php >> /var/log/cron-backup.log 2>&1
Конструкция >> /путь/к/логу 2>&1 добавляет вывод команды в конец указанного файла (>> вместо > сохраняет историю предыдущих запусков) и дополнительно перенаправляет поток ошибок (2) туда же, куда идёт основной вывод (1) — без этой части сообщения об ошибках по-прежнему уходили бы в почту, а не в лог.
MAILTO="admin@example.ru"
0 3 * * * /usr/bin/php /var/www/example.ru/cron/backup.php
Переменная MAILTO, заданная в начале файла crontab, задаёт адрес, куда пересылается вывод всех задач ниже неё — но сама пересылка почты с сервера должна быть корректно настроена (SMTP или локальный почтовый агент), иначе указание MAILTO без рабочей отправки почты ничего не даст.
Системный crontab и /etc/cron.d
Помимо личных crontab пользователей, в системе есть общесистемные способы задать расписание: файл /etc/crontab и директория /etc/cron.d/, куда пакеты и администратор могут добавлять собственные файлы расписания. Ключевое синтаксическое отличие — в этих файлах каждая строка содержит дополнительное поле с именем пользователя, от которого выполняется команда, сразу после пяти полей времени.
# /etc/cron.d/myapp-backup
0 2 * * * www-data /usr/bin/php /var/www/example.ru/cron/backup.php >> /var/log/cron-backup.log 2>&1
Такой подход удобен для задач, специфичных для конкретного приложения или сайта, которые логично хранить рядом с остальной конфигурацией проекта, а не в личном crontab случайного системного пользователя — а также для автоматизации через системы управления конфигурацией вроде Ansible, где проще разложить отдельные файлы в /etc/cron.d, чем редактировать единый crontab.
Systemd timers как современная альтернатива
В дистрибутивах с systemd существует альтернативный механизм планирования — systemd timers, которые запускают systemd-сервисы по расписанию. Это более многословный в настройке способ (требует отдельных unit-файлов .service и .timer), но даёт возможности, которых нет у классического cron: явные зависимости от других сервисов, встроенное логирование через journalctl, повторные попытки при сбое.
# /etc/systemd/system/backup.timer
[Unit]
Description=Ежедневный бэкап в 2:00
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
Параметр Persistent=true гарантирует, что если сервер был выключен в момент, когда должен был сработать таймер, задача выполнится сразу после следующего включения — у классического cron такой возможности нет, что делает systemd timers предпочтительнее для критичных задач на серверах, которые иногда перезагружаются в непредсказуемое время.
Для большинства типичных серверных задач разница между cron и systemd timers не критична, и выбор часто определяется просто личными предпочтениями или тем, что уже привычно администратору — оба механизма надёжно решают задачу периодического выполнения команд на современном Linux-сервере.
Блокировка от одновременного запуска: flock
Если задача может выполняться дольше интервала своего расписания, существует риск, что предыдущий запуск ещё не завершился, когда наступает время следующего — оба процесса начинают работать параллельно и конкурировать за одни и те же ресурсы (например, оба одновременно пишут в один файл или создают дублирующиеся записи в базе данных).
*/5 * * * * /usr/bin/flock -n /tmp/sync-stock.lock /usr/bin/php /var/www/example.ru/cron/sync-stock.php
Утилита flock берёт эксклюзивную блокировку на указанный файл на время выполнения команды — если предыдущий запуск ещё не завершился и блокировка уже занята, флаг -n (non-blocking) немедленно завершает новый запуск вместо ожидания, не позволяя накапливаться очереди из повисших процессов.
Эта техника особенно важна для задач синхронизации данных с внешними системами (остатки на складе, курсы валют, статусы заказов), где два одновременных запуска могут привести не просто к лишней нагрузке, а к реальным ошибкам согласованности данных.
Часовой пояс и cron: скрытый источник путаницы
Cron использует часовой пояс, установленный на уровне операционной системы, если явно не указано иное — если сервер настроен на UTC, а администратор мысленно планирует расписание по московскому времени, все задачи будут выполняться на 3 часа раньше или позже ожидаемого без каких-либо сообщений об ошибке.
timedatectl
crontab -l | head -1
В некоторых системах можно задать часовой пояс прямо в файле crontab через переменную CRON_TZ в начале файла или для отдельной строки — это удобно, если часть задач должна выполняться по времени конкретного региона независимо от системного часового пояса сервера, например для отчётов, синхронизированных с рабочими часами удалённой команды.
CRON_TZ="Europe/Moscow"
0 9 * * 1-5 /usr/bin/php /var/www/example.ru/cron/morning-report.php
Типичные ошибки при работе с cron
Использование относительных путей или команд без полного пути в предположении, что cron унаследует то же окружение, что и интерактивный терминал — переменная PATH в cron значительно уже, и команды, работающие в терминале напрямую, могут не найти нужный интерпретатор или утилиту при запуске из cron.
Незнание о необходимости экранировать символ % обратным слэшем внутри команды — в crontab % без экранирования интерпретируется как перевод строки, что обрывает команду в неожиданном месте, например при использовании date +%Y%m%d прямо в строке crontab.
Отсутствие перенаправления вывода в лог — без явного логирования узнать, что задача давно и стабильно падает с ошибкой, можно только случайно, обнаружив непрочитанные системные письма или отсутствие ожидаемого результата (например, свежих бэкапов).
Планирование слишком частых тяжёлых задач без учёта времени их фактического выполнения — если задача выполняется 20 минут, а запланирована каждые 15 минут, накапливающиеся параллельные запуски могут перегрузить сервер или начать конфликтовать друг с другом за одни и те же ресурсы.
Редактирование crontab напрямую в текстовом файле без команды crontab (например, через прямое редактирование /var/spool/cron/crontabs) — это может привести к тому, что демон cron не подхватит изменения корректно, поскольку ожидает специфичных прав доступа и владельца файла.
Cron внутри Docker-контейнеров
В контейнеризированных приложениях классический подход «cron как системная служба» работать не будет напрямую — минимальные образы Docker часто вообще не содержат установленного cron, а сама философия контейнеров предполагает один основной процесс на контейнер, а не полноценную инициализацию системы со множеством фоновых служб.
Практические варианты решения: установить cron внутрь образа приложения как дополнительный процесс (требует supervisor или аналогичного менеджера процессов, раз в контейнере не может быть системы инициализации в классическом смысле), запустить отдельный служебный контейнер только с cron, который обращается к остальным сервисам по сети, или вынести планирование на уровень оркестратора — например, Kubernetes CronJob, если используется Kubernetes.
FROM php:8.3-cli
RUN apt-get update && apt-get install -y cron
COPY crontab /etc/cron.d/myapp-cron
RUN chmod 0644 /etc/cron.d/myapp-cron && crontab /etc/cron.d/myapp-cron
CMD ["cron", "-f"]
Для большинства небольших проектов, где cron нужен только для пары фоновых задач вроде очистки временных файлов или отправки отчётов, отдельный лёгкий контейнер, полностью посвящённый cron, — самое простое и наименее подверженное неожиданным побочным эффектам решение, не требующее переработки основного образа приложения под управление несколькими процессами сразу.
Мониторинг: как узнать, что задача реально выполняется
Логирования самого по себе недостаточно, если никто не читает лог регулярно, — задача может незаметно перестать выполняться на несколько недель прежде, чем кто-то заметит проблему, обычно уже по факту отсутствия результата (например, отсутствия свежих резервных копий в критический момент).
Простое и практичное решение — внешний сервис мониторинга задач по расписанию (например, healthchecks.io или его self-hosted аналоги): задача при успешном завершении отправляет HTTP-запрос на уникальный URL, и если сервис не получает такой запрос вовремя согласно ожидаемому расписанию, он присылает уведомление о пропущенном запуске.
0 2 * * * /usr/bin/php backup.php && curl -fsS -m 10 https://hc-ping.com/ваш-уникальный-id
Оператор && между командами гарантирует, что сигнал об успехе будет отправлен только если сам скрипт бэкапа завершился без ошибки (с кодом возврата 0) — если скрипт упал, curl не выполнится, сервис мониторинга не получит ожидаемый сигнал вовремя и пришлёт уведомление о пропуске, что и является желаемым поведением при сбое.
Для полностью self-hosted варианта без зависимости от внешнего сервиса можно реализовать похожую логику самостоятельно: скрипт записывает временную метку последнего успешного запуска в файл или в Redis, а отдельная проверка (например, тоже по cron, но реже) сравнивает эту метку с текущим временем и отправляет уведомление, если ожидаемый интервал давно прошёл, а метка не обновлялась.
Чек-лист настройки надёжной cron-задачи
Шаг 1. Используйте полные пути к исполняемым файлам и скриптам в команде — не полагайтесь на переменную PATH, унаследованную из интерактивного терминала.
Шаг 2. Перенаправьте и стандартный вывод, и поток ошибок в лог-файл через >> /путь/к/логу 2>&1, чтобы иметь возможность посмотреть, что произошло при последнем запуске.
Шаг 3. Экранируйте символ % обратным слэшем, если он используется внутри команды, например в формате даты.
Шаг 4. Оцените реальное время выполнения задачи и убедитесь, что интервал расписания больше этого времени, чтобы избежать перекрывающихся параллельных запусков.
Шаг 5. Для критичных задач (бэкапы, продление сертификатов) настройте внешний мониторинг вроде healthchecks.io, который уведомит, если ожидаемый запуск не состоялся.
Шаг 6. Проверьте задачу сразу после добавления, не дожидаясь запланированного времени — временно измените расписание на ближайшую минуту, убедитесь в корректной работе, и только затем верните нужное расписание.
Итог: простой инструмент, требующий аккуратности в деталях
Cron остаётся одним из самых надёжных и проверенных временем инструментов автоматизации на Linux-серверах — простой синтаксис расписания решает подавляющее большинство задач без необходимости в более сложных системах вроде systemd timers или внешних оркестраторов задач.
Главные практические уроки: не полагаться на окружение интерактивного терминала внутри cron-задач, всегда логировать вывод, и для по-настоящему критичных задач добавлять внешний мониторинг, который заметит проблему раньше, чем это сделает разгневанный клиент или пропавший бэкап в критический момент. Если нужна помощь с настройкой автоматизации на сервере — оставьте заявку внизу страницы, поможем настроить надёжное расписание задач под конкретный проект.
При добавлении каждой новой задачи в расписание стоит потратить пару минут на то, чтобы сразу применить эти практики, а не откладывать их на потом «когда будет время» — гораздо проще заложить логирование и мониторинг с самого начала, чем возвращаться к десяткам уже работающих задач спустя месяцы, чтобы навести в них порядок задним числом.
Почему стоит выбрать нас
Быстрый запуск
Подберём конфигурацию и развернём проект в день обращения.
Надёжность
NVMe-диски, защита от DDoS и резервные копии по расписанию.
Поддержка 24/7
Дежурные инженеры на связи в любое время суток.
Настройка под ключ
Установим CMS, настроим веб-сервер, SSL и почту за вас.
Перенос без простоя
Аккуратно перенесём сайт и базу данных на новый сервер.
Прозрачные цены
Актуальные тарифы и честный подбор без навязывания.
Вопросы и ответы
Как посмотреть список текущих задач cron?
Командой crontab -l для задач текущего пользователя; для просмотра задач другого пользователя нужны права root и команда crontab -l -u имя_пользователя.
Почему скрипт работает вручную, но не запускается через cron?
Чаще всего причина в переменной окружения PATH — cron запускает задачи с минимальным набором переменных окружения, отличным от интерактивной сессии терминала, поэтому в самом скрипте или в crontab стоит использовать полные пути к командам и файлам.
Как получать уведомления об ошибках в cron-задачах?
По умолчанию cron отправляет вывод задачи (включая ошибки) на локальную почту пользователя, если не перенаправлен явно — для реальных уведомлений стоит либо настроить пересылку локальной почты на внешний адрес, либо перенаправлять вывод в лог-файл и мониторить его отдельно.
В чём разница между crontab пользователя и /etc/cron.d?
Crontab пользователя редактируется командой crontab -e и не содержит поля с именем пользователя; файлы в /etc/cron.d редактируются напрямую как текстовые файлы и требуют явного указания пользователя, от имени которого выполняется задача, в каждой строке.
Нужно ли перезапускать cron после изменения crontab?
Нет, служба cron автоматически обнаруживает изменения в crontab при сохранении через команду crontab -e и подхватывает новое расписание без перезапуска.
Что лучше — cron или systemd timers?
Для простых периодических задач cron остаётся проще и привычнее; systemd timers предпочтительнее, если нужна интеграция с журналом systemd, явные зависимости от других сервисов или более гибкое управление повторными попытками при сбое.
Как запустить cron внутри Docker-контейнера?
Проще всего вынести cron в отдельный небольшой контейнер, единственная задача которого — выполнять запланированные команды, обращаясь к остальным сервисам приложения по сети, а не пытаться встроить полноценную службу cron в основной контейнер приложения.
Что делает flock в cron-задачах?
flock берёт эксклюзивную блокировку на файл на время выполнения команды и с флагом -n немедленно завершает новый запуск, если предыдущий ещё не закончился, — это защищает от параллельного выполнения одной и той же задачи, если она иногда работает дольше интервала своего расписания.
Как узнать, что cron-задача действительно выполнилась успешно, а не просто была запущена?
Явно логировать код завершения задачи (переменная $? в bash) в отдельный файл или использовать внешний сервис мониторинга задач по расписанию (например, healthchecks.io), который ожидает регулярный сигнал от задачи и уведомляет, если сигнал не пришёл вовремя.
Как оплачивать услуги?
Оплата помесячная, для облачных серверов доступна почасовая тарификация. Принимаются карты, банковские переводы и электронные кошельки.
Есть ли круглосуточная поддержка?
Да, дежурные инженеры на связи 24/7.
Выдаёте ли закрывающие документы?
Да, предоставим необходимые документы для оплаты.
Как оставить заявку?
Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.