Cron — демон планування завдань в Unix-системах, що запускає команди за розкладом. Використовується для резервного копіювання, надсилання звітів, оновлення сертифікатів SSL та будь-якої автоматизації.
Синтаксис cron-запису
# ┌───────────── хвилини (0-59)
# │ ┌─────────── години (0-23)
# │ │ ┌───────── день місяця (1-31)
# │ │ │ ┌─────── місяць (1-12)
# │ │ │ │ ┌───── день тижня (0=Sun, 6=Sat)
# │ │ │ │ │
# * * * * * команда
Приклади розкладів
| Вираз | Коли виконується |
|---|---|
| * * * * * | Щохвилини |
| 0 * * * * | Щогодини (о 00 хвилин) |
| 0 3 * * * | Щодня о 03:00 |
| 0 3 * * 1 | Щопонеділка о 03:00 |
| 0 0 1 * * | Перше число кожного місяця |
| */15 * * * * | Кожні 15 хвилин |
| 0 2,14 * * * | О 02:00 та 14:00 |
| @reboot | При кожному перезавантаженні |
| @daily | Раз на добу (= 0 0 * * *) |
Керування crontab
># Редагувати crontab поточного користувача
crontab -e
# Переглянути поточний crontab
crontab -l
# Видалити crontab
crontab -r
# Crontab для іншого користувача (root)
sudo crontab -u www-data -e
Практичні приклади
># Бекап бази даних кожні 6 годин
0 */6 * * * mysqldump -u root -pPASSWORD mydb > /backup/db_$(date +%Y%m%d_%H%M).sql
# Оновлення сертифікатів Let's Encrypt
0 3 * * * certbot renew --quiet
# Очищення логів старіших за 30 днів
0 4 * * * find /var/log/myapp -name "*.log" -mtime +30 -delete
# Запуск Python-скрипта та логування виводу
*/30 * * * * /usr/bin/python3 /opt/scripts/sync.py >> /var/log/sync.log 2>&1
# Перезапуск сервісу о 05:00 щонеділі
0 5 * * 0 systemctl restart myapp
Змінні оточення в cron
># Cron має мінімальне оточення — $PATH може не містити потрібні шляхи
# Завжди використовуйте повні шляхи до команд
/usr/bin/php, /usr/local/bin/wp тощо
# Або явно задайте PATH на початку crontab:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=admin@example.com # надсилати помилки на email
2>&1 >> /tmp/cron_debug.log до команди, щоб захопити весь вивід. Якщо cron мовчить при явній помилці — найімовірніше проблема з PATH або правами файлу.Синтаксис crontab
# Формат: MIN HOUR DOM MON DOW COMMAND
# MIN = 0-59
# HOUR = 0-23
# DOM = 1-31 (день місяця)
# MON = 1-12
# DOW = 0-7 (день тижня, 0 і 7 = неділя)
# Кожні 5 хвилин
*/5 * * * * /usr/local/bin/check.sh
# Щодня о 3:30
30 3 * * * /usr/local/bin/backup.sh
# Щопонеділка о 2:00
0 2 * * 1 /usr/local/bin/weekly-report.sh
# Перше число кожного місяця опівночі
0 0 1 * * /usr/local/bin/monthly-cleanup.sh
Логування cron-завдань
# Перенаправлення виводу в лог
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Придушення виводу (без логів)
0 3 * * * /usr/local/bin/backup.sh > /dev/null 2>&1
Спеціальні значення
| Значення | Еквівалент |
|---|---|
| @reboot | При запуску системи |
| @daily | 0 0 * * * |
| @weekly | 0 0 * * 0 |
| @monthly | 0 0 1 * * |
Часті помилки в cron-завданнях
Більшість «cron не працює» насправді означає, що cron відпрацював правильно — просто з несподіваним для автора скрипту оточенням.
/bin/sh: 1: wp: not found
Cron запускає команди з мінімальним PATH, який часто не включає директорію, де лежить потрібна утиліта (наприклад, /usr/local/bin для WP-CLI). Використовуйте повний шлях до бінарника: дізнатися його можна командою which wp у звичайній оболонці і підставити результат у crontab.
You (deploy) are not allowed to use this program (crontab)
Користувач не входить до списку /etc/cron.allow, при цьому сам файл існує — якщо cron.allow присутній, доступ до crontab отримують лише перелічені в ньому користувачі, решта блокуються незалежно від прав sudo. Додайте ім'я користувача до файлу або використовуйте sudo crontab -u username -e від адміністратора.
flock: 456: Resource temporarily unavailable
Це не збій, а захист від повторного запуску: попередній екземпляр завдання ще не завершився і тримає файл блокування, тому новий запуск одразу виходить. Якщо це трапляється регулярно — проблема в тому, що завдання виконується довше за інтервал розкладу, а не в самому блокуванні; з'ясовуйте, чому попередній запуск не вкладається у строк, а не прибирайте flock.
cron чи systemd timer: порівняння
| Параметр | cron | systemd timer |
|---|---|---|
| Логування | Вручну у файл | Вбудоване в journald |
| Повтор при збої | Немає | Є (Restart=, OnFailure=) |
| Точність розкладу | До хвилини | До секунди |
| Залежності від інших служб | Немає | Є (After=, Requires=) |
Для простих періодичних завдань cron лишається простішим у налаштуванні. Для завдань із залежностями від інших служб або потребою автоповтору при збої — див. створення systemd unit-файлу.
Коли cron не підходить
Мінімальний інтервал cron — одна хвилина; для завдань, які мають виконуватися раз на кілька секунд, потрібен окремий демон або systemd timer з OnUnitActiveSec=. Сам по собі cron не захищає від паралельного запуску одного й того самого завдання — якщо попередній екземпляр ще виконується, а новий стартує за розкладом, вони працюватимуть одночасно, конкуруючи за ті самі ресурси, якщо явно не обгорнути команду в flock, як у прикладі вище. Для завдань, критичних до доставлення результату (наприклад, обов'язкове надсилання звіту), cron не дає вбудованого механізму повторних спроб — при збої завдання просто не виконається до наступного запуску за розкладом.
Як перевірити результат
Поява завдання в розкладі ще не означає, що воно реально відпрацювало — перевіряйте факт виконання окремо:
# Переконатися, що cron взагалі запускав завдання
grep CRON /var/log/syslog | grep "backup.sh" | tail -5
# Перевірити код завершення останнього запуску
echo $?
# У системах з journald
journalctl -u cron --since "1 hour ago" | grep backup
Сама поява запису в логі означає лише те, що cron запустив команду — не те, що вона завершилася успішно. Явно логуйте код повернення всередині самого скрипту або додайте MAILTO в crontab, щоб отримувати листа при ненульовому коді завершення.