
Cron - демон, который выполняет команды по расписанию. Запускается при старте системы, читает расписание из файлов crontab и проверяет каждую минуту, не пора ли что-нибудь выполнить. Без cron администрирование сервера превращается в ручной труд: бэкапы, очистка логов, обновление данных - всё это можно автоматизировать.
Синтаксис crontab: пять полей и команда
Каждая строка в crontab - одна задача. Формат:
минуты часы день_месяца месяц день_недели команда
Диапазоны каждого поля:
Поле | Диапазон |
|---|---|
Минуты | 0–59 |
Часы | 0–23 |
День месяца | 1–31 |
Месяц | 1–12 |
День недели | 0–7 (0 и 7 - воскресенье) |
Звёздочка * означает "каждое значение". Примеры:
# Каждый день в 02:30
30 2 * * * /usr/bin/backup.sh
# Каждый понедельник в 03:00
0 3 * * 1 /usr/bin/weekly-report.sh
# Каждые 15 минут
*/15 * * * * /usr/bin/check-service.sh
# В первый день каждого месяца в 00:00
0 0 1 * * /usr/bin/monthly-cleanup.sh
# В 12:00 и 18:00 каждый день
0 12,18 * * * /usr/bin/send-digest.sh
Запятая перечисляет несколько значений: 0,15,30,45 * * * * - каждые 15 минут. Дефис задаёт диапазон: 0 9-17 * * 1-5 - каждый час с 9 до 17 по будням.
Редактирование через crontab -e
Не редактируйте файлы crontab напрямую в /var/spool/cron/. Используйте команду:
crontab -e
При первом запуске выберите редактор (nano - проще, vim - для тех, кто привык). Команда открывает crontab текущего пользователя.
Полезные флаги:
crontab -l # посмотреть текущее расписание
crontab -e # редактировать
crontab -r # удалить все задачи (осторожно, без подтверждения)
crontab -u www-data -e # редактировать crontab другого пользователя (нужен root)

Crontab конкретного пользователя хранится в /var/spool/cron/crontabs/username. Системные задачи находятся в /etc/cron.d/ и в директориях /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/.
Специальные символы и сокращения
*/5 * * * * # каждые 5 минут
0 */2 * * * # каждые 2 часа
0 0 */3 * * # каждые 3 дня
0 0 * * 0 # каждое воскресенье в полночь
Cron поддерживает строки-сокращения вместо пяти полей:
Сокращение | Значение |
|---|---|
| При каждом старте системы |
| Раз в год (0 0 1 1 *) |
| Раз в месяц (0 0 1 * *) |
| Раз в неделю (0 0 * * 0) |
| Раз в день (0 0 * * *) |
| Каждый час (0 * * * *) |
Пример с @reboot:
@reboot /usr/bin/start-monitoring.sh
Скрипт запустится при каждой загрузке системы.
Переменные MAILTO и SHELL
По умолчанию cron отправляет вывод выполненных команд по почте пользователю. Это настраивается переменными в начале crontab:
SHELL=/bin/bash
MAILTO=admin@example.com
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# Задачи
0 2 * * * /usr/bin/backup.sh
SHELL=/bin/bash - указывает, каким интерпретатором выполнять команды. По умолчанию /bin/sh, что может вызвать проблемы если скрипты используют bash-специфичный синтаксис.
MAILTO=admin@example.com - куда отправлять вывод команд. Если задача выводит текст в stdout или stderr, cron отправит его на этот адрес.
MAILTO="" - отключить отправку писем полностью. Удобно, когда почта не настроена, а задачи генерируют вывод.
PATH - явно указать пути поиска команд. Cron запускается с минимальным окружением, команды типа python3 или node могут не находиться без явного пути.
Лучшая практика - всегда писать полные пути к командам:
0 3 * * * /usr/bin/python3 /home/deploy/scripts/cleanup.pyПять практических примеров задач
1. Ежедневный бэкап базы данных
0 3 * * * /usr/bin/mysqldump -u root -p'secret' mydb > /backups/mydb_$(date +\%Y-\%m-\%d).sql 2>/dev/null
Знак % в crontab нужно экранировать: \%. Без экранирования cron интерпретирует % как символ новой строки. Бэкап создаётся каждый день в 03:00 с датой в имени файла.
Лучше вынести команду в отдельный скрипт:
# /usr/local/bin/db-backup.sh
#!/bin/bash
BACKUP_DIR="/backups"
DATE=$(date +%Y-%m-%d)
mysqldump -u root -p'secret' mydb > "$BACKUP_DIR/mydb_$DATE.sql"
find "$BACKUP_DIR" -name "*.sql" -mtime +30 -delete
0 3 * * * /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1
>> /var/log/db-backup.log 2>&1 - весь вывод (stdout и stderr) пишется в лог-файл.
2. Очистка временных файлов
0 4 * * 0 find /tmp -type f -mtime +7 -delete
Каждое воскресенье в 04:00 удаляются файлы в /tmp старше 7 дней.
3. Перезапуск сервиса при зависании
*/5 * * * * /usr/local/bin/check-nginx.sh
Скрипт проверяет состояние и перезапускает при необходимости:
#!/bin/bash
if ! systemctl is-active --quiet nginx; then
systemctl start nginx
echo "$(date): nginx restarted" >> /var/log/nginx-monitor.log
fi4. Обновление сертификатов SSL
0 0,12 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"
Дважды в сутки certbot проверяет, нужно ли продлить сертификат. --quiet подавляет вывод при успехе. --deploy-hook перезагружает Nginx после продления.
5. Синхронизация файлов на резервный сервер
30 1 * * * rsync -az --delete /var/www/html/ user@backup-server:/var/www/html/ >> /var/log/rsync.log 2>&1
Каждую ночь в 01:30 содержимое /var/www/html/ синхронизируется на резервный сервер. --delete удаляет на резервном файлы, которые удалены на основном.

Отладка через /var/log/syslog
Когда задача не выполняется - первое место проверки:
grep CRON /var/log/syslog | tail -20
Типичные записи:
May 22 03:00:01 server CRON[12345]: (root) CMD (/usr/local/bin/db-backup.sh)
May 22 03:00:01 server CRON[12346]: (CRON) info (No MTA installed, discarding output)
Первая строка - задача запустилась. Вторая - есть вывод, но нет почтового агента. Чтобы увидеть вывод, перенаправьте его в файл.
На Ubuntu 22.04 логи cron также доступны через journald:
journalctl -u cron --since "today" | grep CMD
Частые причины незапуска задач:
Нет прав на выполнение скрипта (
chmod +x script.sh)Неполный путь к интерпретатору или команде
%в команде не экранированЗадача работает, но молча падает: проверьте
2>&1в перенаправленииПеременная
PATHне содержит нужную директорию
Быстрый тест: запустите команду из crontab вручную от нужного пользователя:
sudo -u www-data /usr/local/bin/my-task.shСравнение с systemd-таймерами
Systemd-таймеры - альтернатива cron на современных Linux-системах. Разница:
Параметр | Cron | Systemd-таймеры |
|---|---|---|
Настройка | Один файл crontab | Два файла: .timer + .service |
Логи | /var/log/syslog | journald (journalctl) |
Зависимости | Нет | Можно задать зависимости от других юнитов |
Пропущенные запуски | Не восстанавливает |
|
Точность | Минута | Секунда и меньше |
Окружение | Минимальное | Полноценное systemd-окружение |
Для большинства задач cron проще и достаточен. Systemd-таймеры полезны, когда нужны зависимости ("запустить после запуска сети"), точность до секунды или удобный просмотр через journalctl.
Пример systemd-таймера для ежедневного бэкапа:
# /etc/systemd/system/db-backup.service
[Unit]
Description=Database backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/db-backup.sh
# /etc/systemd/system/db-backup.timer
[Unit]
Description=Daily database backup timer
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
systemctl enable --now db-backup.timer
systemctl list-timers | grep backup
Persistent=true означает: если система была выключена в момент запуска - задача выполнится при следующем старте.
Управление cron-демоном
systemctl status cron # состояние
systemctl start cron # запустить
systemctl enable cron # добавить в автозапуск
systemctl restart cron # перезапустить
После изменения crontab через crontab -e перезапуск cron не нужен: демон сам отслеживает изменения в файлах расписания.
Итог
Cron - надёжный инструмент с 40-летней историей. Синтаксис из пяти полей покрывает 95% задач автоматизации. Главные правила: полные пути к командам, экранирование %, перенаправление вывода в лог, проверка через /var/log/syslog при проблемах. Для сложных сценариев с зависимостями и мониторингом смотрите в сторону systemd-таймеров.