Crontab: примеры расписания задач в Linux, синтаксис и типичные ошибки
Crontab — это таблица расписания, по которой демон cron запускает команды: каждая строка состоит из пяти полей времени (минута, час, день месяца, месяц, день недели) и команды, например */5 * * * * /opt/backup.sh запускает скрипт каждые 5 минут. Редактируется таблица командой crontab -e, а большинство проблем связано с урезанным окружением cron: указывайте полные пути и перенаправляйте вывод в лог.
Cron — стандартный планировщик задач в Linux: демон cron раз в минуту читает таблицы расписания (crontab) и запускает команды, время которых пришло. Так делают резервные копии по ночам, чистят временные файлы, обновляют сертификаты и отправляют отчёты. Ниже — синтаксис, crontab примеры на типовые случаи и разбор ошибок, из-за которых задача «молча» не выполняется.
Как открыть и редактировать crontab
У каждого пользователя своя таблица. Работают с ней через команду crontab, а не прямым редактированием файлов в /var/spool/cron:
| Команда | Что делает |
|---|---|
crontab -e | открыть свою таблицу в редакторе (при первом запуске предложит выбрать редактор) |
crontab -l | показать текущую таблицу |
crontab -r | удалить таблицу целиком, без подтверждения |
crontab -i -r | удалить с подтверждением (Debian/Ubuntu, RHEL) |
sudo crontab -u alice -e | редактировать таблицу другого пользователя |
crontab файл | заменить таблицу содержимым файла |
После сохранения crontab -e проверяет синтаксис и пишет crontab: installing new crontab. Если в строке ошибка, вы увидите errors in crontab file, can't install и предложение отредактировать снова. Перезапускать демон не нужно — изменения подхватываются автоматически.
Будьте осторожны с -r: буквы e и r на клавиатуре рядом, а резервной копии таблицы нет. Полезная привычка — держать бэкап:
crontab -l > ~/crontab.bak
# восстановить
crontab ~/crontab.bak
Синтаксис crontab: пять полей времени
Каждая строка задачи выглядит так:
# ┌───────── минута (0–59)
# │ ┌─────── час (0–23)
# │ │ ┌───── день месяца (1–31)
# │ │ │ ┌─── месяц (1–12 или jan–dec)
# │ │ │ │ ┌─ день недели (0–7 или sun–sat; 0 и 7 — воскресенье)
# │ │ │ │ │
* * * * * команда
В каждом поле можно использовать специальные символы:
| Символ | Значение | Пример |
|---|---|---|
* | любое значение | * * * * * — каждую минуту |
, | список | 0 9,18 * * * — в 9:00 и 18:00 |
- | диапазон | 0 9-18 * * * — каждый час с 9 до 18 |
/ | шаг | */15 * * * * — каждые 15 минут |
Шаг можно сочетать с диапазоном: 0 8-20/2 * * * — каждые два часа с 8 до 20 (8, 10, …, 20). Время берётся по часовому поясу системы — проверьте его командой timedatectl.
Crontab примеры на каждый день
| Расписание | Строка crontab |
|---|---|
| каждую минуту | * * * * * |
| каждые 5 минут | */5 * * * * |
| каждые 30 минут | */30 * * * * или 0,30 * * * * |
| каждый час в начале часа | 0 * * * * |
| каждые 6 часов | 0 */6 * * * |
| каждый день в 3:00 | 0 3 * * * |
| каждый день в 23:45 | 45 23 * * * |
| по будням в 9:00 | 0 9 * * 1-5 |
| по выходным в полдень | 0 12 * * 6,0 |
| по понедельникам в 8:30 | 30 8 * * 1 |
| 1-го числа каждого месяца в 00:00 | 0 0 1 * * |
| раз в квартал | 0 0 1 1,4,7,10 * |
| раз в год, 1 января | 0 0 1 1 * |
| каждые 10 минут в рабочее время | */10 9-18 * * 1-5 |
Полная строка задачи — расписание плюс команда:
# резервная копия базы каждую ночь в 3:00
0 3 * * * /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1
# чистка временных файлов старше 7 дней по воскресеньям
0 4 * * 0 /usr/bin/find /tmp/app -type f -mtime +7 -delete
# проверка сайта каждые 5 минут
*/5 * * * * /usr/bin/curl -fsS https://example.com/health > /dev/null || /usr/local/bin/alert.sh
Специальные строки @reboot, @daily и другие
Вместо пяти полей можно написать короткий псевдоним:
| Псевдоним | Эквивалент |
|---|---|
@reboot | один раз при старте демона cron (после загрузки системы) |
@hourly | 0 * * * * |
@daily / @midnight | 0 0 * * * |
@weekly | 0 0 * * 0 |
@monthly | 0 0 1 * * |
@yearly / @annually | 0 0 1 1 * |
@reboot /home/alice/bin/start-tunnel.sh >> /home/alice/tunnel.log 2>&1
Для сервисов, которые должны работать постоянно и перезапускаться при падении, @reboot — слабое решение: лучше оформить юнит systemd с Restart=on-failure.
Как запустить задачу каждые N секунд
Минимальная единица cron — одна минута. Если нужно чаще, есть два приёма. Первый — несколько строк со сдвигом через sleep:
* * * * * /opt/check.sh
* * * * * sleep 30; /opt/check.sh
Второй, более правильный — таймер systemd с OnUnitActiveSec=30s. Если задача выполняется чаще раза в минуту, это обычно уже работа для демона или таймера, а не для cron.
Системный crontab: /etc/crontab и /etc/cron.d
Кроме пользовательских таблиц есть системные. Их формат отличается: между временем и командой стоит имя пользователя, от которого запускать команду.
# /etc/cron.d/certbot-renew
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root certbot renew --quiet
Что где лежит:
/etc/crontab— главная системная таблица; в Debian/Ubuntu из неё запускаются каталоги ниже./etc/cron.d/— отдельные файлы с задачами, удобно класть их пакетами или через Ansible./etc/cron.hourly/,cron.daily/,cron.weekly/,cron.monthly/— сюда кладут исполняемые скрипты без расписания; их запускаетrun-parts(или anacron).
Подвох с run-parts: в Debian и Ubuntu он пропускает файлы, в имени которых есть точка. Скрипт backup.sh в /etc/cron.daily/ не запустится — назовите его backup. Проверить, что будет выполнено, можно командой run-parts --test /etc/cron.daily.
Файлы в /etc/cron.d/ должны принадлежать root, не быть доступными на запись группе и остальным и оканчиваться переводом строки — иначе cron их проигнорирует.
Окружение cron: PATH, SHELL и переменные
Главная причина фразы «в терминале работает, а в cron нет» — окружение. Cron запускает команды через /bin/sh, без вашего .bashrc и .profile, с минимальным PATH (обычно /usr/bin:/bin). Поэтому docker, node или php из /usr/local/bin могут быть просто не найдены.
Переменные можно задать в начале таблицы:
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
CRON_TZ=Europe/Moscow
0 6 * * * cd /srv/app && ./report.sh
MAILTO— куда отправлять вывод задачи;MAILTO=""отключает письма.CRON_TZ— часовой пояс для расписания (поддерживается cronie в RHEL/Fedora; в Debian-версии cron эта переменная не работает).- Подстановок вроде
$HOME/binв значениях переменных нет — пишите пути целиком.
Самый надёжный вариант — указывать в задаче абсолютные пути ко всем программам и файлам, а в скриптах первой строкой ставить shebang: #!/bin/bash.
Где смотреть логи cron
Cron записывает в журнал сам факт запуска задачи, но не её вывод:
# Debian/Ubuntu
grep CRON /var/log/syslog
# через systemd-journald
journalctl -u cron --since today # Debian/Ubuntu
journalctl -u crond --since today # RHEL, Fedora, Rocky
# RHEL без journald
tail -f /var/log/cron
Строка запуска выглядит примерно так:
Oct 14 03:00:01 web1 CRON[21544]: (root) CMD (/usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1)
Чтобы видеть, что задача напечатала и с какой ошибкой упала, перенаправляйте и stdout, и stderr в файл: >> /var/log/job.log 2>&1. Без этого вывод уходит письмом через локальную почту, а если почтовый агент не установлен — теряется (в журнале появится No MTA installed, discarding output).
Почему cron не запускает задачу: типичные ошибки
Команда не найдена
Симптом: в логе задачи command not found. Причина — урезанный PATH. Решение: полный путь к программе (which docker подскажет его) или строка PATH= в начале таблицы.
Знак процента в команде
В crontab символ % означает перевод строки: всё после первого % передаётся команде на стандартный ввод. Поэтому классическая строка с датой в имени файла ломается:
# неправильно — команда обрежется на первом %
0 2 * * * tar czf /backup/site-$(date +%F).tar.gz /var/www
# правильно — экранировать
0 2 * * * tar czf /backup/site-$(date +\%F).tar.gz /var/www
Ещё проще — вынести логику в скрипт, где % не нужно экранировать.
Скрипт не исполняемый или с неправильными переводами строк
Ошибка Permission denied значит, что у файла нет бита x: выполните chmod +x script.sh или запускайте явно, /bin/bash /opt/script.sh. Если скрипт писали в Windows, переводы строк CRLF дают странную ошибку /bin/bash^M: bad interpreter — исправляется командой sed -i 's/\r$//' script.sh или dos2unix.
Относительные пути и рабочий каталог
Задача стартует в домашнем каталоге пользователя. Если скрипт читает ./config.ini, он будет искать его в ~. Делайте cd /srv/app && перед командой или используйте абсолютные пути внутри скрипта.
День месяца и день недели одновременно
Если заданы оба поля, cron срабатывает, когда совпадает любое из них, а не оба сразу. Строка 0 9 13 * 5 запустится 13-го числа и каждую пятницу, а не только в пятницу 13-е. Нужное условие проверяют внутри команды:
0 9 13 * * [ "$(date +\%u)" = 5 ] && /opt/friday13.sh
Задачи накладываются друг на друга
Если резервная копия идёт дольше интервала, следующий запуск стартует поверх предыдущего. Защита — блокировка через flock:
*/10 * * * * /usr/bin/flock -n /tmp/sync.lock /opt/sync.sh
С флагом -n второй экземпляр сразу завершится, если первый ещё работает.
Демон cron не запущен
Проверьте systemctl status cron (в RHEL — crond). В минимальных образах, например в Docker-контейнерах, демона cron нет вовсе — задачи в crontab там просто некому выполнять.
Как отладить задачу cron
Запустите команду в окружении, максимально похожем на cron:
env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME=$HOME /bin/sh -c '/opt/backup.sh'
Если здесь возникает ошибка — она же будет и в cron. Временно поставьте расписание * * * * *, перенаправьте вывод в файл и следите за ним через tail -f. Для проверки самого расписания помогают онлайн-калькуляторы выражений cron, но последнее слово — за журналом системы.
Кто может пользоваться cron: cron.allow и cron.deny
Доступ ограничивают файлы /etc/cron.allow и /etc/cron.deny. Если существует cron.allow, crontab доступен только перечисленным в нём пользователям. Если его нет, а есть cron.deny, — всем, кроме перечисленных. Пользователь из запрещённых при crontab -e получит сообщение You (bob) are not allowed to use this program.
Итог
Crontab — это строки из пяти полей времени и команды: */5 * * * * — каждые 5 минут, 0 3 * * * — каждую ночь, 0 9 * * 1-5 — по будням. Редактируйте таблицу через crontab -e, указывайте абсолютные пути, экранируйте %, пишите вывод в лог и защищайте долгие задачи через flock. Разобранных crontab примеров хватает для большинства серверных задач, а автоматизация и планирование задач подробно разобраны на практике в курсе по Linux.
Частые вопросы
Чем cron отличается от anacron?
Cron запускает задачи в точное время и пропускает их, если компьютер был выключен. Anacron гарантирует, что ежедневные, еженедельные и ежемесячные задачи выполнятся после включения, поэтому подходит для ноутбуков и десктопов.
Что лучше: cron или таймеры systemd?
Cron проще и есть почти везде. Таймеры systemd дают логи в journalctl, зависимости, ограничение ресурсов и параметр Persistent=true для пропущенных запусков, поэтому на современных серверах их всё чаще выбирают для важных задач.
Где физически хранится crontab пользователя?
В Debian и Ubuntu — в /var/spool/cron/crontabs/имя, в RHEL и Fedora — в /var/spool/cron/имя. Редактировать эти файлы напрямую не стоит, используйте crontab -e.
Как временно отключить задачу в crontab?
Закомментируйте строку символом # в начале через crontab -e. Чтобы отключить все задачи пользователя, сохраните таблицу в файл командой crontab -l и затем удалите её через crontab -r.
Как запустить задачу в последний день месяца?
Отдельного поля для этого нет. Запускайте задачу 28–31 числа и проверяйте, что завтра первое число: 0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /opt/job.sh.
Эта тема подробно разобрана с практикой в курсе «Linux: с нуля до уверенного администрирования».
Перейти к курсу