Начать
Документация

Шпаргалка по синтаксису cron

Пять полей, шесть спецсимволов и пара ловушек, на которых спотыкаются все. Ниже — таблицы, готовые выражения и то же самое в формате systemd.

Пять полей строки crontab

┌───────────── минута (0–59)
│ ┌─────────── час (0–23)
│ │ ┌───────── день месяца (1–31)
│ │ │ ┌─────── месяц (1–12 или JAN–DEC)
│ │ │ │ ┌───── день недели (0–7 или SUN–SAT, 0 и 7 — воскресенье)
│ │ │ │ │
* * * * *  команда
Поле Диапазон Примечание
Минута0–59
Час0–230 — полночь
День месяца1–3131-е бывает не каждый месяц
Месяц1–12, JAN–DECрегистр не важен
День недели0–7, SUN–SATи 0, и 7 — воскресенье

Спецсимволы

Символ Значение Пример
*любое значение поля* * * * * — каждую минуту
,перечисление0 9,18 * * * — в 9:00 и 18:00
-диапазон0 9-18 * * * — каждый час с 9 до 18
/шаг внутри диапазона*/15 * * * * — каждые 15 минут
Lпоследний день (не во всех cron)0 3 L * * — в последний день месяца
Wближайший будний день (не во всех cron)0 3 15W * * — рабочий день рядом с 15-м
#n-й день недели месяца (не во всех cron)0 3 * * 1#2 — второй понедельник

L, W и # — расширения Quartz. Классический Vixie cron из Debian и Ubuntu их не понимает; CronAlive принимает стандартные пять полей и сокращения ниже.

Сокращения

Сокращение Эквивалент
@yearly, @annually0 0 1 1 *
@monthly0 0 1 * *
@weekly0 0 * * 0
@daily, @midnight0 0 * * *
@hourly0 * * * *
@rebootодин раз при старте системы

Частые расписания

Когда Выражение
каждую минуту* * * * *
каждые 5 минут*/5 * * * *
каждые 30 минут0,30 * * * *
каждый час в :1515 * * * *
каждые 2 часа0 */2 * * *
ежедневно в 3:3030 3 * * *
дважды в день, 6:00 и 18:000 6,18 * * *
по будням в 9:000 9 * * 1-5
по выходным в 10:000 10 * * 6,0
каждый понедельник в 4:000 4 * * 1
1-го числа в 0:000 0 1 * *
каждые 15 минут в рабочие часы*/15 9-18 * * 1-5

Типовые ошибки

Шаг больше диапазона

*/60 * * * *не «раз в час». Шаг применяется внутри диапазона поля: минуты — это 0–59, и единственное подходящее значение — 0. Выражение запустится в начале каждого часа, но по случайности, а не по смыслу. Правильно: 0 * * * *. Так же */70 не сработает вовсе.

День месяца вместе с днём недели

Когда оба поля заданы (не *), cron объединяет их по ИЛИ, а не по И. Выражение 0 3 13 * 5 сработает и 13-го числа любого месяца, и каждую пятницу — а не только в пятницу 13-го. Это поведение исторически заложено в Vixie cron; чтобы получить «И», приходится проверять дату внутри скрипта.

Воскресенье как 0 и 7

Оба значения валидны, а вот 1-7 означает «понедельник–воскресенье», то есть всю неделю, а не «будни». Будни — 1-5.

Таймзона

Cron работает в таймзоне системы (или в CRON_TZ, если он задан в начале crontab). Сервер в UTC, а расписание вы читаете как московское — минус три часа к ожиданиям. В чеке CronAlive таймзона задаётся явно, включая переходы на летнее время.

Знак процента

В crontab % — это перевод строки, а не символ. Строка с date +%Y-%m-%d оборвётся на первом же проценте. Экранируйте: \%.

PATH и окружение

У cron минимальный PATH и нет вашего профиля: ~/.bashrc не читается. Пишите абсолютные пути — и к своей задаче, и к curl.

То же самое в systemd (OnCalendar)

Таймеры systemd постепенно вытесняют cron. Формат другой: день-недели ГГГГ-ММ-ДД ЧЧ:ММ:СС, где * — любое значение, а / — шаг.

Когда cron OnCalendar
каждую минуту* * * * **-*-* *:*:00
каждые 5 минут*/5 * * * **:0/5
каждый час0 * * * *hourly
ежедневно в 3:3030 3 * * **-*-* 03:30:00
по будням в 9:000 9 * * 1-5Mon..Fri 09:00
1-го числа в 0:000 0 1 * *monthly

Проверить выражение и увидеть ближайшие запуски:

systemd-analyze calendar 'Mon..Fri 09:00' --iterations=3

У таймеров есть то, чего нет у cron: Persistent=true навёрстывает пропущенный запуск после выключения машины, а RandomizedDelaySec= разносит одновременные задачи. Пример юнита с пингами — в сниппетах.

Проверьте, что задача действительно запускается

Корректное выражение ничего не гарантирует: cron мог не перечитать crontab, задача — упасть на первой строке, а сервер — уйти в ребут. Создайте чек с тем же расписанием, и вы узнаете о молчании в тот же час: мониторинг cron-джобов за 5 минут.