VDS · Приложения и боты

Задания по расписанию: cron и systemd-таймеры

Как запускать задачи по расписанию, почему задание работает руками и молчит в cron, и когда стоит перейти на systemd-таймеры.

Обновлено 23 августа 2026 г.

Резервные копии, продление сертификатов, очистка временных файлов, ночные отчёты — всё это задачи по расписанию. В Linux их решают двумя способами: классическим cron и таймерами systemd.

Cron: быстрый старт

Расписание пользователя правится так:

crontab -e
crontab -l

Формат строки — пять полей и команда:

# мин  час  день  месяц  день-недели  команда
  0    4    *     *      *            /usr/local/bin/backup.sh
  */15 *    *     *      *            /usr/local/bin/check.sh
  30   3    *     *      0            /usr/bin/systemctl restart myapp

Полезные сокращения: @daily, @hourly, @reboot. Проверить, что задания вообще запускались:

grep CRON /var/log/syslog | tail -30
journalctl -u cron --since "1 hour ago"

Почему скрипт работает руками, но не в cron

Это самая частая проблема, и причина почти всегда одна из трёх.

Другое окружение. У cron минимальный PATH и нет ваших переменных из .bashrc. Пишите полные пути к программам и задавайте переменные явно в начале скрипта или в crontab:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Другой каталог. Задание стартует в домашнем каталоге пользователя, относительные пути ведут не туда. Всегда используйте абсолютные пути.

Вывод уходит в никуда. Ошибка была, но её никто не увидел. Пишите журнал:

0 4 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Ещё две мелочи: символ % в строке cron нужно экранировать как \%, а в файле crontab обязателен перевод строки в конце — иначе последнее задание игнорируется.

Проверять, что задание отработало, нужно снаружи. Молчащий cron выглядит точно так же, как успешный. Настройте push-проверку в Uptime Kuma: скрипт в конце дёргает адрес, и если сутки не дёргал — приходит тревога.

Таймеры systemd

Когда задача сложнее одной команды, таймер удобнее: у него есть журнал, зависимости, ограничения ресурсов и защита от одновременного запуска двух копий.

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Run nightly backup

[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=true
RandomizedDelaySec=15m

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers
journalctl -u backup.service -n 50

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

Что стоит учесть

  • Время на машине — UTC по умолчанию. Проверьте timedatectl перед тем, как ставить «в 4 утра».
  • Тяжёлые задачи (архивы, проверка копий) упираются в диск. См. скорость диска и ставьте их на ночь.
  • Долгая задача, запускаемая каждые пять минут, наложится сама на себя. В systemd поможет Type=oneshot, в cron — блокировка через flock:
*/5 * * * * /usr/bin/flock -n /tmp/job.lock /usr/local/bin/job.sh