Cron — демон планирования задач в Unix-системах, запускающий команды по расписанию. Используется для резервного копирования, отправки отчётов, обновления сертификатов SSL и любой автоматизации.
Синтаксис cron-записи
# ┌───────────── минуты (0-59)
# │ ┌─────────── часы (0-23)
# │ │ ┌───────── день месяца (1-31)
# │ │ │ ┌─────── месяц (1-12)
# │ │ │ │ ┌───── день недели (0=Sun, 6=Sat)
# │ │ │ │ │
# * * * * * команда
Примеры расписаний
| Выражение | Когда выполняется |
|---|---|
| * * * * * | Каждую минуту |
| 0 * * * * | Каждый час (в 00 минут) |
| 0 3 * * * | Каждый день в 03:00 |
| 0 3 * * 1 | Каждый понедельник в 03:00 |
| 0 0 1 * * | Первое число каждого месяца |
| */15 * * * * | Каждые 15 минут |
| 0 2,14 * * * | В 02:00 и 14:00 |
| @reboot | При каждой перезагрузке |
| @daily | Раз в сутки (= 0 0 * * *) |
Управление crontab
># Редактировать crontab текущего пользователя
crontab -e
# Посмотреть текущий crontab
crontab -l
# Удалить crontab
crontab -r
# Crontab для другого пользователя (root)
sudo crontab -u www-data -e
Практические примеры
># Бэкап базы данных каждые 6 часов
0 */6 * * * mysqldump -u root -pPASSWORD mydb > /backup/db_$(date +%Y%m%d_%H%M).sql
# Обновление Let's Encrypt сертификатов
0 3 * * * certbot renew --quiet
# Очистка логов старше 30 дней
0 4 * * * find /var/log/myapp -name "*.log" -mtime +30 -delete
# Запуск Python-скрипта и логирование вывода
*/30 * * * * /usr/bin/python3 /opt/scripts/sync.py >> /var/log/sync.log 2>&1
# Перезапуск сервиса в 05:00 каждое воскресенье
0 5 * * 0 systemctl restart myapp
Переменные окружения в cron
># Cron имеет минимальное окружение — $PATH может не содержать нужные пути
# Всегда используйте полные пути к командам
/usr/bin/php, /usr/local/bin/wp и т.д.
# Или явно задайте PATH в начале crontab:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=admin@example.com # отправлять ошибки на email
2>&1 >> /tmp/cron_debug.log к команде для захвата всего вывода. Если cron молчит при явной ошибке — скорее всего проблема с PATH или правами файла.Синтаксис crontab
# Формат: MIN HOUR DOM MON DOW COMMAND
# MIN = 0-59
# HOUR = 0-23
# DOM = 1-31 (день месяца)
# MON = 1-12
# DOW = 0-7 (день недели, 0 и 7 = воскресенье)
# Каждые 5 минут
*/5 * * * * /usr/local/bin/check.sh
# Каждый день в 3:30
30 3 * * * /usr/local/bin/backup.sh
# Каждый понедельник в 2:00
0 2 * * 1 /usr/local/bin/weekly-report.sh
# Первое число каждого месяца в полночь
0 0 1 * * /usr/local/bin/monthly-cleanup.sh
Логирование cron-заданий
# Перенаправление вывода в лог
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Подавление вывода (без логов)
0 3 * * * /usr/local/bin/backup.sh > /dev/null 2>&1
Специальные значения
| Значение | Эквивалент |
|---|---|
| @reboot | При запуске системы |
| @daily | 0 0 * * * |
| @weekly | 0 0 * * 0 |
| @monthly | 0 0 1 * * |
Частые ошибки в cron-заданиях
Большинство «cron не работает» на самом деле cron отработал правильно — просто с неожиданным для автора скрипта окружением.
/bin/sh: 1: wp: not found
Cron запускает команды с минимальным PATH, который часто не включает директорию, где лежит нужная утилита (например, /usr/local/bin для WP-CLI). Используйте полный путь к бинарнику: узнать его можно командой which wp в обычной оболочке и подставить результат в crontab.
You (deploy) are not allowed to use this program (crontab)
Пользователь не входит в список /etc/cron.allow, при этом сам файл существует — если cron.allow присутствует, доступ к crontab получают только перечисленные в нём пользователи, остальные блокируются независимо от прав sudo. Добавьте имя пользователя в файл или используйте sudo crontab -u username -e от администратора.
flock: 456: Resource temporarily unavailable
Это не сбой, а защита от повторного запуска: предыдущий экземпляр задачи ещё не завершился и держит файл блокировки, поэтому новый запуск сразу выходит. Если это происходит регулярно — проблема в том, что задача выполняется дольше интервала расписания, а не в самой блокировке; разбирайтесь, почему предыдущий запуск не укладывается в срок, а не убирайте flock.
cron или systemd timer: сравнение
| Параметр | cron | systemd timer |
|---|---|---|
| Логирование | Вручную в файл | Встроено в journald |
| Повтор при сбое | Нет | Есть (Restart=, OnFailure=) |
| Точность расписания | До минуты | До секунды |
| Зависимости от других служб | Нет | Есть (After=, Requires=) |
Для простых периодических задач cron остаётся проще в настройке. Для задач с зависимостями от других служб или необходимостью автоповтора при сбое — см. создание systemd unit-файла.
Когда cron не подходит
Минимальный интервал cron — одна минута; для задач, которые должны выполняться раз в несколько секунд, нужен отдельный демон или systemd timer с OnUnitActiveSec=. Cron сам по себе не защищает от параллельного запуска одной и той же задачи — если предыдущий экземпляр ещё выполняется, а новый стартует по расписанию, они будут работать одновременно, конкурируя за одни и те же ресурсы, если явно не обернуть команду в flock, как в примере выше. Для задач, критичных к доставке результата (например, обязательная отправка отчёта), cron не даёт встроенного механизма ретраев — при сбое задача просто не выполнится до следующего запуска по расписанию.
Как проверить результат
Появление задачи в расписании ещё не значит, что она реально отработала — проверяйте факт выполнения отдельно:
# Убедиться, что cron вообще запускал задачу
grep CRON /var/log/syslog | grep "backup.sh" | tail -5
# Проверить код завершения последнего запуска
echo $?
# На системах с journald
journalctl -u cron --since "1 hour ago" | grep backup
Само по себе появление записи в логе означает только то, что cron запустил команду — не то, что она завершилась успешно. Явно логируйте код возврата внутри самого скрипта или добавьте MAILTO в crontab, чтобы получать письмо при ненулевом коде завершения.