cron в Linux: расписание задач, crontab и альтернативы на Ubuntu
cron — классический планировщик задач Unix. Запускает команды по расписанию: каждую минуту, ежедневно, по сложным паттернам времени. Разбираем синтаксис crontab, разницу между пользовательским и системным cron, типичные ошибки с PATH и окружением, как читать логи cron и когда лучше выбрать systemd timer вместо классического cron.
Что такое cron
cron — демон-планировщик задач в Unix-системах. На Ubuntu установлен по умолчанию (пакет cron), запускается под systemd (cron.service), читает таблицы расписания (crontab) и запускает указанные команды в заданное время.
Для чего обычно используется:
- Регулярные бэкапы (например, каждый день в 3:00).
- Ротация логов (если не через
logrotate). - Периодическая чистка временных файлов.
- Поллинг внешних API и обновление кэшей.
- Health-чеки и алерты.
- Перезапуск сервисов по графику.
В системах с systemd многие задачи мигрируют на systemd timer — он мощнее, но cron остаётся стандартом «в каждой Linux-системе».
Где живут расписания
На Ubuntu есть несколько источников cron-задач:
| Файл/директория | Кто запускает | Назначение |
|---|---|---|
crontab -e (пользовательский) |
от имени этого пользователя | задачи конкретного пользователя |
/etc/crontab |
system-wide, нужно поле user | системный планировщик |
/etc/cron.d/* |
system-wide, нужно поле user | пакеты кладут сюда свои задания |
/etc/cron.hourly/* |
root | скрипты, запускаемые каждый час |
/etc/cron.daily/* |
root | каждый день (обычно 6:25 утра) |
/etc/cron.weekly/* |
root | каждую неделю |
/etc/cron.monthly/* |
root | каждый месяц |
В директориях cron.hourly/daily/weekly/monthly лежат исполняемые файлы, а не crontab-формат. Просто скрипт с chmod +x.
Синтаксис crontab
Каждая строка — одна задача:
* * * * * команда
│ │ │ │ │
│ │ │ │ └── день недели (0–7, 0 и 7 = воскресенье)
│ │ │ └──── месяц (1–12)
│ │ └────── день месяца (1–31)
│ └──────── час (0–23)
└────────── минута (0–59)
Примеры:
# Каждую минуту
* * * * * /usr/local/bin/check.sh
# Каждый час в 30-ю минуту
30 * * * * /usr/local/bin/hourly-task.sh
# Каждый день в 03:00
0 3 * * * /usr/local/bin/backup.sh
# Каждый понедельник в 9 утра
0 9 * * 1 /usr/local/bin/weekly-report.sh
# Первого числа каждого месяца в полночь
0 0 1 * * /usr/local/bin/monthly-archive.sh
Спецсимволы
* # любое значение
*/N # каждые N единиц (например, */5 в минуте = каждые 5 минут)
N-M # диапазон (1-5 = с 1 по 5)
N,M,K # список (1,15,30)
Сложные примеры:
# Каждые 15 минут
*/15 * * * * /usr/local/bin/poll.sh
# В рабочее время (с 9 до 18 по будням)
0 9-18 * * 1-5 /usr/local/bin/business-hours.sh
# В 5 и 35 минут каждого часа
5,35 * * * * /usr/local/bin/twice-per-hour.sh
# Каждые 2 часа с 0:00
0 */2 * * * /usr/local/bin/every-two-hours.sh
Алиасы
В современных cron поддерживаются удобные алиасы:
@reboot # один раз при загрузке системы
@yearly # 0 0 1 1 *
@annually # синоним @yearly
@monthly # 0 0 1 * *
@weekly # 0 0 * * 0
@daily # 0 0 * * *
@midnight # синоним @daily
@hourly # 0 * * * *
@daily /usr/local/bin/backup.sh
@reboot /usr/local/bin/init-cache.sh
@reboot особенно полезен — запускает команду один раз при старте системы, без необходимости создавать systemd-сервис.
Редактирование crontab пользователя
# Открыть свой crontab в редакторе
crontab -e
# Список текущих задач
crontab -l
# Удалить весь crontab
crontab -r
# Из чужого имени (только под root)
sudo crontab -u www-data -e
При первом запуске crontab -e спросит выбор редактора (nano, vim, etc.). Сменить редактор:
export EDITOR=nano
crontab -e
После сохранения файл валидируется. Если синтаксис неправильный — будет ошибка, изменения не примутся.
Системный crontab и /etc/cron.d
В пользовательском crontab нет поля «пользователь» — задача всегда запускается от того, кто её владелец.
В системном /etc/crontab и /etc/cron.d/* есть дополнительное поле:
* * * * * user команда
Пример /etc/cron.d/backup-app:
# m h dom mon dow user command
0 3 * * * deploy /usr/local/bin/backup-app.sh
/etc/cron.d/ — стандартное место для размещения cron-задач пакетами (например, logrotate, unattended-upgrades). Свои задачи туда тоже можно класть, но обычно удобнее crontab -e или systemd timer.
Логирование cron
На Ubuntu cron пишет в системный journal:
# Все cron-события за последний час
journalctl -u cron --since "1 hour ago"
# В реальном времени
journalctl -u cron -f
# Подробно по конкретному пользователю
journalctl _UID=$(id -u www-data) --grep cron
В старом синтаксисе (Ubuntu 18.04 и старше) логи cron в /var/log/syslog. На Ubuntu 24.04 уже только journald.
Важно: cron логирует факт запуска, но не stdout/stderr задачи. Чтобы видеть вывод задачи, перенаправляйте в файл:
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
>> file 2>&1 — добавлять и stdout, и stderr в один файл.
PATH и окружение в cron
Главная боль cron — минимальное окружение:
# В crontab переменные среды не наследуются от пользовательской shell-сессии!
# PATH = /usr/bin:/bin
# Никаких ~/.bashrc, ~/.profile.
Это значит, что команды, которые работают в shell, могут не работать в cron, если они зависят от расширенного PATH. Решения:
# Вариант 1: использовать полные пути
0 3 * * * /usr/local/bin/python3 /home/deploy/script.py
# Вариант 2: установить PATH в начале crontab
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOME=/home/deploy
0 3 * * * python3 /home/deploy/script.py
Также cron не загружает ~/.bashrc. Если ваш скрипт требует переменных оттуда — экспортируйте их явно в начале скрипта или через source:
#!/bin/bash
source /home/deploy/.profile
python3 /home/deploy/script.py
anacron — для ноутбуков
Классический cron работает только если система включена в момент запуска. Если задача запланирована на 3:00, а ноутбук в это время спал — задача не выполнится.
anacron решает это: проверяет при старте, не пропущены ли запланированные задачи, и запускает их.
sudo apt install anacron
sudo nano /etc/anacrontab
Формат отличается — поле «период в днях» вместо точного времени:
# period in days delay job-id command
1 5 cron.daily run-parts /etc/cron.daily
7 10 cron.weekly run-parts /etc/cron.weekly
1 5 cron.daily — выполнять каждый день, через 5 минут после старта (если не выполнялось сегодня).
На серверах anacron обычно не нужен — они круглосуточно онлайн.
systemd timer как современная альтернатива
Для новых проектов часто выбирают systemd timer вместо cron:
# /etc/systemd/system/backup.service
[Unit]
Description=Backup application data
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=deploy
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 3am
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=300
Persistent=true
[Install]
WantedBy=timers.target
Активация:
sudo systemctl enable --now backup.timer
sudo systemctl list-timers
Преимущества systemd timer:
- Логи через journalctl — нативно, не нужно перенаправлять stdout.
Persistent=true— пропущенные запуски выполняются после возвращения системы (как anacron).RandomizedDelaySec— разброс времени запуска, чтобы не нагружать ресурсы одномоментно.- Зависимости от других unit-ов — можно запускать только если запущен другой сервис.
- Ограничения ресурсов — CPU, память через [Service]-секцию.
Минусы:
- Сложнее одного crontab-вижу-всё.
- Требует два файла на одну задачу.
Используйте cron для простых задач, systemd timer — для важных, требующих надёжной логики и пропуск-recovery.
Распространённые ошибки
Скрипт работает руками, в cron — нет
Скорее всего, разный PATH или окружение. Запустите тест:
* * * * * env > /tmp/cron-env.txt
И сравните с env в обычной shell. Все недостающие переменные — установите в начале crontab или в скрипте.
% в команде
% в crontab — это перенос строки в данных. Чтобы использовать буквально (например, в формате даты):
0 3 * * * mysqldump db > /backup/db-$(date +\%Y\%m\%d).sql
# ↑ экранировать!
Cron работает в UTC
На Ubuntu по умолчанию cron использует системную таймзону. Проверить:
timedatectl
# Time zone: UTC (UTC, +0000) ← если так, расписание в UTC
# Сменить
sudo timedatectl set-timezone Europe/Moscow
sudo systemctl restart cron
Облачные VPS часто идут с UTC — не забывайте про сдвиг при настройке расписания.
Многострочный crontab
Каждая задача — одна строка. Перенос через \ в crontab не работает:
# Сломается:
0 3 * * * /usr/local/bin/long-command \
--arg1 --arg2
# Правильно: одной строкой
0 3 * * * /usr/local/bin/long-command --arg1 --arg2
# Или вынести в скрипт
0 3 * * * /usr/local/bin/wrapper.sh
Безопасность cron
- Скрипты, запускаемые из cron, должны быть с правильными правами. Особенно если запускаются от root.
- Не пишите секреты прямо в crontab — лежит в открытом виде.
- Используйте
set -eв bash-скриптах, чтобы они падали на первой ошибке, а не делали полусломанные действия. - Перенаправляйте stderr — иначе ошибки уходят в почту root, которая на сервере обычно не настроена и переполняется.
Частые вопросы
Как открыть crontab для редактирования?
crontab -e для своего пользователя или sudo crontab -u <username> -e для другого. Команда откроет файл в редакторе (nano, vim — выбор делается при первом запуске). После сохранения изменения применяются автоматически.
В чём разница между crontab и /etc/cron.d?
crontab -e — пользовательские задачи, хранятся в /var/spool/cron/crontabs/<user>. /etc/cron.d/* — системные задачи, размещаются файлами (один файл = одна задача или их набор). В /etc/cron.d нужно указывать поле user в строке. Удобство: пакеты кладут свои cron-задачи в /etc/cron.d, чтобы при удалении пакета задачи тоже удалялись.
Как запустить задачу каждые 5 минут?
*/5 * * * * команда. Звёздочка-слэш-N означает «каждые N единиц». Аналогично: */15 каждые 15 минут, 0 */2 * * * каждые 2 часа в начале часа.
Почему cron не запускает мой скрипт?
Самые частые причины: (1) скрипт не имеет права на исполнение (chmod +x); (2) в скрипте используются команды без полного пути (cron PATH сильно урезан); (3) скрипт ждёт чего-то от stdin или интерактивно; (4) указана не та таймзона. Проверяйте логи: journalctl -u cron --since "1 hour ago".
Что лучше — cron или systemd timer?
Для простых задач (бэкап раз в день) — оба нормально. Для критичных, требующих надёжной логики (пропуск-recovery, зависимости, ограничения ресурсов, нативное логирование) — systemd timer. На современных Ubuntu-системах timer постепенно вытесняет cron в новых проектах, но cron остаётся стандартом и не уходит.
Что запомнить
crontab -eдля своих задач,/etc/cron.d/*для системных.- Формат:
минута час день_месяца месяц день_недели команда. - Спецсимволы:
*,*/N,N-M,N,M,K. - Алиасы:
@reboot,@daily,@hourly. - PATH в cron минимальный — используйте полные пути или задайте PATH в начале crontab.
- Перенаправляйте stdout и stderr:
>> file 2>&1. - Логи:
journalctl -u cron -f. - Для пропуск-recovery — anacron или systemd timer с
Persistent=true. - Cron — простой и проверенный; systemd timer — мощнее, для критичных задач.