Cron-задача не запускается: что проверить
Вручную команда работает, строка в crontab выглядит правильно, а в 3 часа ночи ничего не происходит. Cron редко объясняет причину: интерфейса у него нет, а ошибки по умолчанию уходят в локальный почтовый ящик, который никто не читает. Пройдите этот список сверху вниз — первые пункты встречаются чаще всего.
1. Работает ли cron вообще?
systemctl status cron # Debian, Ubuntu systemctl status crond # RHEL, Fedora, Rocky, Alma
В Docker-контейнере демона cron обычно нет, пока вы его сами не запустите; подробнее в руководстве cron-задачи в Docker.
2. Пытался ли cron запустить задачу?
Cron записывает каждый запуск. Если вашей строки в журнале нет, cron её и не запускал; если есть, задача стартовала и упала сама.
journalctl -u cron --since "today" # или -u crond grep CRON /var/log/syslog # Debian, Ubuntu с rsyslog sudo tail /var/log/cron # семейство RHEL
3. Окружение почти пустое
Самая частая причина. Cron не читает .bashrc и .profile. Задача получает SHELL=/bin/sh, короткий PATH вроде /usr/bin:/bin и ни одной из ваших переменных. Поэтому node, php, docker или aws из /usr/local/bin или менеджера версий просто не находятся.
- Пишите полные пути:
/usr/local/bin/node /srv/app/job.js. Путь покажетcommand -v node. - Или задайте путь в начале crontab:
PATH=/usr/local/bin:/usr/bin:/bin. - Секреты загружайте явно, например
set -a; . /srv/app/.env; set +aв скрипте-обёртке.
Чтобы повторить окружение cron в своей консоли, запустите задачу с пустым окружением:
env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME="$HOME" /bin/sh -c '/srv/app/job.sh'
4. Знак процента обрезает команду
В crontab % означает перевод строки, и всё после первого такого знака передаётся команде на вход. date +%F молча ломает строку. Экранируйте его как \% или вынесите команду в скрипт:
0 3 * * * /srv/backup.sh > /var/backups/log-$(date +\%F).txt 2>&1
5. Скрипт нельзя выполнить
- Нет права на выполнение.
chmod +x /srv/job.sh. - Нет shebang. Первой строкой должно быть
#!/bin/bashили#!/bin/sh. Синтаксис Bash, запущенный черезsh, падает в Debian, гдеsh— это dash. - Переводы строк Windows. Скрипт с CRLF падает с
/bin/bash^M: bad interpreter. Исправляется командойsed -i 's/\r$//' job.sh. - Относительные пути. Cron запускает задачи в домашнем каталоге пользователя, а не рядом со скриптом. Строка
cd /srv/app || exit 1в начале скрипта это исправляет.
6. Сам файл crontab
- В
/etc/crontabи файлах из/etc/cron.d/есть поле пользователя:0 3 * * * root /srv/job.sh. В личном crontab (crontab -e) его нет. Перепутаете — строка не сработает. - В Debian и Ubuntu файлы в
/etc/cron.d/игнорируются, если в имени есть точка, а скрипты в/etc/cron.daily/с именемbackup.shпропускаются по той же причине. Называйте ихbackup. - В файле, установленном с диска, после последней строки нужен перевод строки, иначе она может быть проигнорирована.
/etc/cron.allowи/etc/cron.denyмогут запрещать пользователю cron.
7. Расписание не такое, как кажется
- Часовой пояс. Cron работает по часовому поясу сервера, на облачных машинах это часто UTC. Его покажет
timedatectl. После смены пояса перезапустите cron. - День месяца и день недели вместе. Если заданы оба поля, задача запускается, когда совпадает любое из них:
0 0 1 * 1сработает 1-го числа и каждый понедельник. - Шаг.
*/45в поле минут — это запуск в :00 и :45, а не каждые 45 минут.
Вставьте выражение в расшифровщик cron-выражений, чтобы увидеть ближайшие запуски словами.
8. Ошибки уходят в никуда
Cron отправляет вывод задачи письмом пользователю или на MAILTO. Без почтового сервера он теряется. На время отладки пишите его в файл:
*/5 * * * * /srv/job.sh >> /var/log/job.log 2>&1
Если ничего не помогло, добавьте временную строку * * * * * env > /tmp/cron-env, подождите минуту и сравните файл с выводом env в своей консоли.
Узнавать в следующий раз без проверок
Всё перечисленное ломается молча. Чтобы не узнавать об этом через несколько недель, пусть задача сообщает о каждом запуске, а вы получаете алерт, если сообщение не пришло. Создайте проверку в Heartcron с тем же расписанием и добавьте пинг в конец строки:
0 3 * * * /srv/backup.sh && curl -fsS -m 10 --retry 5 -o /dev/null https://ping.heartcron.com/<uuid>
Если cron остановится, сервер пересоберут без crontab или скрипт упадёт, пинг не придёт, и вы получите алерт в Telegram, Slack, Discord или на почту. Чтобы видеть ещё и текст ошибки, отправляйте вывод и код завершения:
0 3 * * * /srv/backup.sh 2>&1 | tail -c 100000 | curl -fsS -m 10 --data-binary @- https://ping.heartcron.com/<uuid>/${PIPESTATUS[0]}
Для этой строки нужен SHELL=/bin/bash в начале crontab: PIPESTATUS есть только в Bash. Или установите открытую утилиту heartcron, она сделает то же самое: heartcron run nightly-backup -- /srv/backup.sh.