Cron и автоматизация задач на VPS
Резервная копия «когда вспомню», проверка сертификата «как-нибудь потом» — ручные задачи рано или поздно забываются. Cron делает их регулярными и незаметными.
Как устроен cron
На сервере работает демон cron — он просыпается раз в минуту, читает список задач и запускает те, для которых наступило время. Задачи хранятся в двух местах: персональный crontab каждого пользователя (свой у root, свой у обычного пользователя) и системный /etc/crontab вместе с файлами в /etc/cron.d/. Ничего сложнее не происходит — cron не «умный планировщик» с очередями и приоритетами, а простой цикл: минута прошла, сверил расписание, запустил команду.
Демон уже установлен и включён на большинстве Ubuntu-образов. Проверить это можно так:
Редактируем crontab
Для задач текущего пользователя используется команда:
При первом запуске она попросит выбрать редактор (обычно удобно оставить nano). Файл открывается пустым или с комментариями-подсказками — расписания добавляются построчно.
Важно понимать: crontab пользователя root и crontab обычного пользователя — это два разных файла. Если выполнить crontab -e от имени своего пользователя, задача будет запускаться с его правами и окружением. Для системных задач — бэкап всей директории /var/www, обновление пакетов, работа с логами демонов — обычно используют root, потому что только у него хватает прав на нужные файлы:
Посмотреть, что уже стоит в расписании, можно без редактирования:
sudo crontab -l
Синтаксис расписания
Каждая строка crontab — это пять полей времени и команда:
│ │ │ │ │
│ │ │ │ └─ день недели (0-6, 0 = воскресенье)
│ │ │ └─── месяц (1-12)
│ │ └────── день месяца (1-31)
│ └───────── час (0-23)
└──────────── минута (0-59)
Звёздочка означает «любое значение». Несколько практических примеров, которые покрывают большинство задач:
# каждый день в 3:00 ночи
*/15 * * * * /usr/local/bin/healthcheck.sh
# каждые 15 минут
0 4 * * 0 /usr/local/bin/weekly-cleanup.sh
# каждое воскресенье в 4:00
Ночные часы для тяжёлых задач выбраны не случайно: меньше нагрузки на диск и сеть, если в это время нет посетителей сайта.
Логирование заданий
Частая ошибка новичков — команда прекрасно работает в терминале, но «ничего не происходит» по расписанию. Причина в том, что cron запускает задачи в минимальном окружении: без переменных из .bashrc, часто с урезанным PATH, не в домашней директории. То, что интерактивная оболочка находит автоматически, cron может просто не увидеть.
Поэтому вывод любой задачи стоит перенаправлять в файл — иначе результат работы (включая ошибки) уходит в почту root или пропадает молча:
>> дописывает вывод в конец файла, а 2>&1 перенаправляет туда же поток ошибок — без этой части скрипт может годами падать с ошибкой, а лог будет выглядеть пустым.
Типовые сценарии
Несколько заданий, которые почти всегда есть смысл поставить на VPS:
# ежедневный бэкап (см. отдельный гайд про резервные копии)
0 5 * * 1 docker system prune -f >> /var/log/docker-cleanup.log 2>&1
# раз в неделю чистим неиспользуемые образы и слои Docker
30 3 * * * certbot renew --quiet >> /var/log/certbot-renew.log 2>&1
# обновление сертификата — но сначала проверьте, не установлен ли свой таймер
Про certbot renew стоит сказать отдельно: пакет certbot чаще всего сам ставит себе systemd-таймер или задачу в /etc/cron.d/ при установке. Дублировать её вручную не нужно — проверьте, нет ли она уже там, прежде чем добавлять свою:
ls /etc/cron.d/ | grep certbot
Для ротации логов приложения обычно достаточно системного logrotate (он и сам запускается через cron/systemd), но если приложение пишет логи в нестандартном формате, простой скрипт архивации и удаления старых файлов раз в сутки решает задачу без лишних зависимостей.
systemd timers как альтернатива
Для более сложных случаев — когда одна задача должна запускаться только после успешного завершения другой, или когда нужно логирование из коробки через journalctl — существуют systemd timers. Они гибче cron, но и настраиваются через пару дополнительных unit-файлов (.service + .timer). Для подавляющего большинства задач на обычном VPS — бэкапы, очистка, проверки — простого cron вполне достаточно, и нет смысла усложнять там, где не требуется.
Проверяем, что задача реально выполняется
После добавления записи в crontab не стоит просто надеяться, что всё заработает. Cron пишет о своих запусках в системный журнал:
# на системах без /var/log/syslog:
journalctl -u cron --since "1 hour ago"
Там будет видно, что задача была запущена в ожидаемое время и с каким кодом завершения. Это отдельная проверка от самого лог-файла задачи — syslog подтверждает факт запуска, а /var/log/backup.log и подобные файлы показывают, что именно произошло внутри скрипта.
Типовые ошибки
- Относительные пути в скрипте — cron запускает задачи не из домашней директории, поэтому
cdв начале скрипта или абсолютные пути обязательны. - Команда работает в терминале, но не в cron — почти всегда дело в другом
PATH: в crontab он короче, и утилиты вродеdockerилиcertbotмогут быть не найдены по имени без полного пути. - Забытый
2>&1— ошибки скрипта уходят в никуда, и задание месяцами «работает», хотя на самом деле падает на первой же строке. - Часовой пояс сервера отличается от ожидаемого — расписание «в 3 ночи» может оказаться днём, если сервер живёт в UTC; проверить и при необходимости поменять можно через
timedatectl. - Слишком частые тяжёлые задачи — бэкап или docker prune каждые 5 минут создают лишнюю нагрузку; для большинства сценариев хватает суточного или недельного расписания.
Что дальше
Cron закрывает регулярное обслуживание сервера, но сам процесс выкладки новых версий кода можно автоматизировать отдельно: автоматизируем сам процесс деплоя через GitHub Actions →