Планировщик задач Crontab: примеры настройки и синтаксис
- Синтаксис crontab: пять полей и одна команда
- Команды управления: -e, -l, -r
- @-алиасы: расписание для людей
- Переменные окружения и MAILTO
- Практические cron примеры
- Отладка: когда задача не запускается
- Безопасность и конкурентный доступ
- Альтернативные каталоги: cron.d, cron.daily и компания
- Cron vs systemd-таймеры: когда что использовать
- Полезные приёмы напоследок
Вы вздрагиваете от звонка мониторинга — диск забит логами, сервис лежит, клиенты шлют гневные письма. А ведь достаточно было одной строчки в crontab, чтобы ротация логов работала сама. Планировщик задач Linux — та штука, которая превращает хаос ручных операций в тихую автоматику. И crontab настройка занимает ровно столько времени, сколько нужно, чтобы заварить чай.
Crontab (от «cron table») — файл расписания для демона cron. Этот демон проверяет задачи раз в минуту и запускает то, что пора запустить. Корни инструмента уходят в 1975 год — он появился ещё в Unix V7. С тех пор cron стал стандартом для всех Unix-подобных систем: от Debian на домашнем сервере до RHEL в корпоративном ЦОДе.
Синтаксис crontab: пять полей и одна команда
Каждая строка в crontab — это расписание в формате «когда» + «что делать». Формат такой:
┌───────────── минута (0–59)
│ ┌───────────── час (0–23)
│ │ ┌───────────── день месяца (1–31)
│ │ │ ┌───────────── месяц (1–12)
│ │ │ │ ┌───────────── день недели (0–7, где 0 и 7 = воскресенье)
│ │ │ │ │
* * * * * /path/to/command
Звёздочка означает «любое значение». Запятая разделяет конкретные значения, дефис задаёт диапазон, косая черта — шаг. Комбинации тоже работают: 1-15/3 значит «каждый третий день с 1-го по 15-е». Разберём на живых примерах:
| Расписание | Что делает |
|---|---|
| 30 2 * * * | Запуск в 2:30 ночи ежедневно |
| 0 */4 * * * | Каждые 4 часа в :00 |
| 0 9 * * 1-5 | В 9:00 по будням |
| 15,45 * * * * | В :15 и :45 каждого часа |
| 0 0 1,15 * * | В полночь 1-го и 15-го числа |
Синтаксис лаконичен, но покрывает 95% задач. Выполнение скрипта по расписанию сводится к одной строке — без GUI, без мастеров настройки, без XML-конфигов.
Команды управления: -e, -l, -r
Работа с пользовательским crontab идёт через утилиту crontab:
- crontab -e — открывает файл расписания в редакторе ($EDITOR). Утилита проверяет синтаксис перед сохранением, так что опечатка не сломает уже работающие задачи.
- crontab -l — выводит текущий файл на экран. Удобно для быстрой проверки или экспорта: crontab -l > backup-crontab.txt.
- crontab -r — удаляет весь файл расписания. Без подтверждения, без корзины. Будьте аккуратны.
- crontab -u username -e — редактирование чужого crontab (нужен root). Полезно при настройке задач для сервисных учётных записей.
Есть ещё системный файл /etc/crontab — он отличается от пользовательского дополнительным полем username между расписанием и командой:
# /etc/crontab
0 3 * * * root /usr/local/bin/backup.sh
Здесь явно указано, от какого пользователя запускать задачу. В пользовательских crontab (через crontab -e) этого поля нет — задача запускается от имени владельца файла.
@-алиасы: расписание для людей
Помимо пяти полей, cron поддерживает удобные сокращения. Они не дают ничего нового по функционалу, но экономят время и снижают риск ошибки:
| Алиас | Эквивалент | Когда запускается |
|---|---|---|
| @reboot | — | При загрузке системы |
| @hourly | 0 * * * * | В начале каждого часа |
| @daily | 0 0 * * * | В полночь |
| @weekly | 0 0 * * 0 | В полночь воскресенья |
| @monthly | 0 0 1 * * | В полночь 1-го числа |
| @yearly | 0 0 1 1 * | 1 января в полночь |
@reboot — особенно полезная вещь. Запуск VPN-тоннеля, поднятие tmux-сессии, прогрев кэша после перезагрузки — всё это удобно вешать именно на этот алиас. В отличие от Windows Task Scheduler, где для аналогичного результата нужно пройти через мастер создания задачи, указать триггер, действие и условия — здесь всё умещается в одну строку. Кстати, crontab поддерживает запуск задач от имени конкретных пользователей через поле username в /etc/crontab — в Windows для этого нужно заводить отдельные учётные записи в планировщике и хранить пароли.
У @reboot есть нюанс: задача запускается в момент старта демона cron, а не в момент загрузки ядра. Если cron стартует раньше сетевого стека, Ваш скрипт не сможет обратиться к внешним сервисам. Для таких сценариев надёжнее использовать systemd-юниты с зависимостью After=network-online.target.
Переменные окружения и MAILTO
Cron запускает задачи в минимальном окружении. PATH будет отличаться от того, к чему Вы привыкли в интерактивном терминале. HOME может указывать не туда, куда ожидаете. Переменных из .bashrc и .bash_profile не будет вовсе — cron их не загружает. Частая ошибка — скрипт работает руками, но падает в cron. Причина почти всегда кроется в окружении.
Попробуйте сами: выполните env -i /bin/sh -c 'echo $PATH' и сравните с тем, что показывает echo $PATH в обычном терминале. Разница будет ощутимой — cron работает примерно в таких же спартанских условиях.
Решения два. Первое — прописывать полные пути к бинарникам внутри скриптов (/usr/bin/python3 вместо python3). Второе — задать PATH прямо в crontab:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
MAILTO=admin@example.com
0 3 * * * /opt/scripts/backup.sh
Переменная MAILTO — ещё одна полезная штука. По умолчанию cron отправляет весь stdout и stderr задачи на почту владельцу crontab. Если вывод не нужен, можно перенаправить его в /dev/null:
А если нужно получать только ошибки — перенаправьте только stdout:
Так stderr всё ещё будет приходить на почту, а обычный вывод отбросится.
Практические cron примеры
Теория без практики мертва. Вот набор задач, которые покрывают типичные сценарии:
Бэкап MySQL ежедневно в 2:00:
Здесь есть нюанс с экранированием % через \% — cron интерпретирует символ процента как перевод строки. Всё, что стоит после первого неэкранированного %, попадёт на stdin команды, а не в аргументы. Это одна из тех ловушек, на которых спотыкаются даже опытные админы. Если бэкап-скрипт сложнее одной строки — вынесите логику в отдельный .sh-файл и вызывайте его из crontab. Так проще и отлаживать, и поддерживать. А чтобы сами бэкапы не стали слабым звеном, стоит заранее продумать выбор сервера для хранения резервных копий — от этого зависит, насколько быстро удастся восстановить данные в случае аварии.
Мониторинг свободного места, предупреждение при заполнении > 90%:
Проверка каждые 10 минут — достаточный интервал, чтобы поймать стремительно растущий лог до того, как диск забьётся на 100%. Если хотите мониторить несколько точек монтирования, создайте отдельный скрипт с циклом по df и порогами для каждого раздела.
Очистка временных файлов старше 7 дней:
Запуск в 4 утра по воскресеньям — период минимальной нагрузки на большинстве серверов. Флаг -delete эффективнее конструкции с -exec rm, так как не порождает отдельный процесс на каждый файл.
Синхронизация файлов между серверами каждые 6 часов:
Отладка: когда задача не запускается
Задача добавлена, синтаксис верный, но ничего не происходит. Знакомая ситуация? Чаще всего проблема сидит в одном из пяти мест, и найти её можно за пару минут, если знать, куда смотреть.
Первый шаг — загляните в лог. На большинстве дистрибутивов cron пишет в /var/log/cron или /var/log/syslog. Быстрый способ проверить:
Если cron вообще не упоминает Вашу задачу — значит, он её не видит. Проверьте, загрузился ли демон: systemctl status cron (или crond на CentOS/RHEL). Убедитесь, что Вы редактировали crontab нужного пользователя. Если Вы добавляли задачу через sudo crontab -e, она попала в crontab root'а, а не Вашего пользователя.
Если в логе видно, что cron запускает задачу, но результата нет — проблема в самом скрипте. Добавьте логирование вывода:
Перенаправление 2>&1 критически важно — без него stderr улетит в почту (или в никуда, если MAILTO не настроен), и Вы никогда не узнаете, что именно сломалось.
Типичные причины, по которым cron-задачи молча падают:
- Нет прав на выполнение. chmod +x script.sh — банально, но часто забывают.
- Относительные пути. Cron запускает задачу из домашнего каталога пользователя. Если скрипт ожидает конкретную рабочую директорию — добавьте cd /path && ./script.sh.
- Окружение. Уже обсуждали выше: PATH в cron минимальный. Используйте env -i /bin/bash -c 'your_command' для тестирования скрипта в похожих условиях.
- Экранирование %. Если в команде есть % без обратного слэша — cron обрежет строку.
Безопасность и конкурентный доступ
На продакшн-сервере crontab — потенциальная точка входа для атак. Если скомпрометированный пользователь может создавать cron-задачи, он способен запускать что угодно с периодичностью в минуту. Два инструмента помогают контролировать ситуацию: файлы доступа и блокировки.
Файлы cron.allow и cron.deny (лежат в /etc/) контролируют, кто вообще имеет право использовать crontab. Логика работы такая: если существует cron.allow — доступ есть только у перечисленных в нём пользователей, а cron.deny игнорируется. Если cron.allow нет, но есть cron.deny — заблокированы только указанные в нём. Если нет ни того, ни другого — поведение зависит от дистрибутива. В Debian доступ открыт всем, а в Red Hat и его производных — только root.
Рекомендация для продакшна: создайте cron.allow и добавьте туда только тех пользователей, которым crontab действительно нужен. Это лучше, чем пытаться перечислить всех «лишних» в cron.deny.
flock решает другую проблему — конкурентный запуск. Представьте: задача бэкапа выполняется 40 минут, а интервал запуска — 30 минут. Без защиты два экземпляра скрипта работают параллельно, пишут в один файл, дерутся за ресурсы. Результат — повреждённый бэкап и нагрузка на диск. Решение:
Флаг -n означает «не ждать, выйти сразу, если лок занят». Так второй экземпляр просто не запустится, пока первый работает. Есть и альтернативный режим — -w timeout, который ждёт указанное количество секунд и сдаётся, если лок не освободился. Для долгих задач с непредсказуемым временем выполнения -n безопаснее.
Альтернативные каталоги: cron.d, cron.daily и компания
Помимо пользовательских crontab-файлов, существует набор системных каталогов, и у каждого своя специфика:
- /etc/cron.d/ — сюда кладут файлы в формате /etc/crontab (с полем username). Удобно для пакетов — при установке софт просто бросает свой файл сюда, при удалении — убирает. Каждый файл обрабатывается отдельно, так что ошибка в одном не затрагивает остальные.
- /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/ — каталоги для скриптов, которые запускает run-parts. Здесь не нужен синтаксис crontab — достаточно положить исполняемый файл. Но есть подвох: имя файла не должно содержать точку. Скрипт backup.sh не запустится — run-parts пропускает файлы с расширениями. Назовите его просто backup или backup-db.
Тонкость: скрипты в cron.daily и подобных каталогах запускает не сам cron напрямую, а anacron (на десктопных системах) или запись в /etc/crontab. Время запуска настраивается в /etc/crontab или /etc/anacrontab. Anacron полезен для машин, которые не работают круглосуточно — он «подхватывает» пропущенные задачи после включения.
Cron vs systemd-таймеры: когда что использовать
На systemd-дистрибутивах (а это сейчас подавляющее число серверных ОС) есть альтернатива — systemd-таймеры. Они решают те же задачи, но с другими компромиссами:
| Критерий | cron | systemd-таймеры |
|---|---|---|
| Сложность настройки | Одна строка | Два файла (.timer + .service) |
| Логирование | /var/log/cron, MAILTO | journalctl, встроенное |
| Зависимости | Нет | Можно указать зависимости от сервисов |
| Точность | 1 минута | До секунды, поддержка монотонных таймеров |
| Запуск пропущенных | Только через anacron | Встроенный Persistent=true |
| Управление ресурсами | Нет | cgroups, лимиты CPU/RAM через unit-файлы |
Для простых периодических задач cron выигрывает за счёт лаконичности: одна строка вместо двух файлов с десятками строк каждый. Но если нужен контроль ресурсов, зависимости от сервисов или посекундная точность — systemd-таймеры дают больше гибкости.
Перевод задачи из cron в systemd выглядит так. Вместо строки 0 3 * * * /opt/scripts/backup.sh создаются два файла:
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
Затем systemctl enable --now backup.timer — и готово. Громоздко? Да. Зато journalctl -u backup.service покажет полный вывод задачи с временными метками, а systemctl list-timers — когда была последняя и следующая активация.
Полезные приёмы напоследок
Рандомная задержка. Если десять серверов одновременно ломятся на бэкап-хранилище в 3:00, пропускная способность делится на всех. NFS-шара стонет, rsync-сессии таймаутятся, а мониторинг рисует красные графики. Добавьте случайную задержку:
Это раскидает запуски по 15-минутному окну.
Логирование с временными метками. Голый >> в файл не покажет, когда задача выполнялась:
Crontab под контролем версий. Храните эталонный crontab в Git и деплойте через crontab /path/to/crontab-file. Так Вы всегда знаете, кто и когда менял расписание. Ansible, Salt, Puppet — все умеют управлять crontab-записями, что логично для инфраструктуры с десятками серверов. При использовании Git удобно держать crontab-файлы рядом с самими скриптами — в одном репозитории и в одном ревью.
Онлайн-валидаторы. Если синтаксис cron-выражений вызывает сомнения — воспользуйтесь сервисами вроде crontab.guru. Вбиваете выражение, получаете расшифровку человеческим языком и список ближайших запусков. Экономит нервы, особенно при хитрых расписаниях с комбинацией диапазонов и шагов.
Cron — инструмент из 1975 года, который пережил контейнеры, облака и Kubernetes. В k8s есть CronJob, в systemd — таймеры, в облаках — Lambda и Cloud Scheduler. Но если у Вас стоит Linux-сервер и нужно запускать скрипт по расписанию — crontab настройка по-прежнему делает это за минуту, без зависимостей и оверхеда. Полвека — и ни одного лишнего абстрактного слоя. Для утилиты командной строки это, пожалуй, лучшая рекомендация из возможных.


