Linux

Как настроить cron на Ubuntu: расписание, журнал и защита от повторного запуска

Создайте учебную задачу cron, проверьте реальный запуск от нужного пользователя и настройте блокировку flock.

Часы, сервер и последовательное выполнение задач

Cron запускает команды по расписанию: создаёт отчёты, обновляет данные, вызывает служебные скрипты и помогает автоматизировать обслуживание сайта. Но запись в crontab ещё не доказывает, что задача выполняется. Важны пользователь, окружение, рабочие каталоги, журнал и поведение при повторном запуске.

Ниже — последовательность для Ubuntu 24.04 с безопасной учебной задачей. Сначала добьёмся реального запуска по расписанию, затем разберём типичные ошибки. Для готовой схемы резервирования с systemd есть отдельная инструкция о ежедневных копиях: не включайте два независимых расписания одного бэкапа одновременно.

Выбрать пользователя и проверить службу

Задача пользователя выполняется с его правами. Обычная публикация отчёта или запуск команды приложения обычно не требуют root. Если на VPS пока есть только root, создайте отдельного пользователя с sudo, а дальнейшую учебную задачу выполняйте из его сеанса.

whoami
printf '%s\n' "$HOME"
systemctl is-active cron
command -v crontab
command -v flock

На минимальном образе нужные пакеты могут отсутствовать:

sudo apt update
sudo apt install cron util-linux
sudo systemctl enable --now cron

Эти команды относятся к cron в Ubuntu. У контейнера или другого дистрибутива может быть другая схема запуска. В Docker чаще удобнее отдельный планировщик либо задача на хосте, а не попытка запускать несколько несвязанных демонов внутри контейнера приложения. Базовую архитектуру показывает руководство Docker.

Создать короткий скрипт

Не начинайте проверку расписания с удаления файлов или тяжёлой копии базы. Сначала запишите дату и имя пользователя:

mkdir -p "$HOME/bin" "$HOME/.cache"
cat > "$HOME/bin/hostfera-cron-demo.sh" <<'SH'
#!/bin/sh
set -eu
/usr/bin/date --iso-8601=seconds
/usr/bin/id -un
SH
chmod 700 "$HOME/bin/hostfera-cron-demo.sh"

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

Здесь явно указан /bin/sh, команды вызываются по полным путям. Скрипт не зависит от алиасов интерактивного Bash и не требует перехода в какой-либо каталог. Это уменьшает число причин, по которым «в терминале работает, а в cron — нет».

Выполнить вручную в похожем окружении

"$HOME/bin/hostfera-cron-demo.sh"
env -i HOME="$HOME" PATH=/usr/bin:/bin \
  /bin/sh "$HOME/bin/hostfera-cron-demo.sh"

Оба запуска должны показать текущую дату и одного ожидаемого пользователя. Во втором случае окружение намеренно сокращено. Так легче обнаружить зависимость от переменной, которую вы экспортировали только в личном терминале.

Если настоящий скрипт использует Python venv, указывайте путь к его интерпретатору, например /opt/myapp/.venv/bin/python, и рабочий каталог приложения. Для Node.js аналогично используйте фактический путь к Node, а не предполагайте, что cron прочитает настройки nvm. Развёртывание окружения описано в статьях FastAPI и Node.js.

Добавить расписание

Сохраните текущий список, если он уже существует:

crontab -l

Если задания есть, выгрузите их в отдельный файл перед правкой. Для первого crontab сообщение no crontab for ... нормально. Командой crontab -e откройте редактор и добавьте:

SHELL=/bin/sh
PATH=/usr/bin:/bin
* * * * * /usr/bin/flock -n "$HOME/.cache/hostfera-cron.lock" "$HOME/bin/hostfera-cron-demo.sh" >> "$HOME/hostfera-cron.log" 2>&1

Оставьте перевод строки в конце файла. Учебная задача запускается каждую минуту. После проверки замените расписание нужным или удалите именно эту строку. Постоянный минутный тест создаёт ненужный журнал.

Редактирование через crontab -e сохраняет остальные задания пользователя. Не используйте echo ... | crontab - без сохранения прежнего содержимого: такой способ может заменить весь существующий список одной новой командой.

Разобрать пять полей времени

Порядок полей: минута, час, день месяца, месяц, день недели. После них находится команда.

Расписание Результат
*/5 * * * * Каждые пять минут по границам минут
15 3 * * * Ежедневно в 03:15
0 6 * * 1 По понедельникам в 06:00
30 2 1 * * Первого числа каждого месяца в 02:30

*/5 — не «через пять минут после добавления». Например, выполнение приходится на минуты 00, 05, 10 и далее. Если одновременно ограничить день месяца и день недели, семантика cron может оказаться неожиданной: в обычном Debian/Ubuntu cron эти два ограничения сочетаются по правилу «или». Для сложного календаря удобнее рассмотреть systemd timer.

Посмотрите timedatectl, чтобы понимать часовой пояс сервера. Не меняйте его на работающем проекте только ради одного задания. Учитывайте также переходы времени в выбранной зоне. Поддержку отдельных расширений вроде CRON_TZ проверяйте по документации именно установленного cron, а не случайного онлайн-генератора.

Подтвердить фактический запуск

После границы следующей минуты проверьте:

crontab -l
tail -n 6 "$HOME/hostfera-cron.log"

В журнале должны появиться новые пары «дата — пользователь». На учебном VPS задача действительно выполнилась от hf_lessons: это отдельная учётная запись стенда, а не пользователь WordPress. Имя на вашем сервере будет своим.

Cron выполняет команду от пользователя hf_lessons
Расписание, записи фактического запуска и пользователь задачи на учебном VPS, 03.11.2026.

Сообщение cron о запуске команды полезно, но не заменяет прикладной результат. Для копии PostgreSQL результат — корректный дамп и проверка восстановления, а не только строка CRON в системном журнале. Такой пример разобран в инструкции pg_dump.

Не допустить пересечения запусков

В примере используется flock -n. Пока один процесс держит блокировку, второй завершается без ожидания. На стенде это проверено отдельными параллельными запусками: второй процесс получил код 1, пока первый удерживал тот же lock-файл.

Lock-файл должен быть один и тот же для всех запусков конкретной задачи и доступен выбранному пользователю. Наличие файла на диске не означает, что блокировка сейчас занята: блокировка связана с работающим процессом. Удалять файл для «разблокировки» обычно не нужно.

Для задач на нескольких серверах локальный flock не становится распределённым замком. У каждого VPS собственная файловая система. Если одну задачу могут одновременно запустить разные узлы, нужен общий механизм координации на уровне приложения или хранилища.

Если cron молчит или пишет ошибку

Проверяйте причины по очереди:

  1. Другой пользователь. sudo crontab -e редактирует задания root, обычный crontab -e — текущего пользователя.
  2. Нет каталога или прав. Пользователь должен читать скрипт, проходить родительские каталоги и писать журнал.
  3. Неверный путь. Относительные пути и интерактивные алиасы лучше убрать.
  4. Не задано окружение. Загрузите нужные настройки безопасным способом внутри скрипта; не размещайте секреты в публичном URL.
  5. Особый символ %. В строке crontab он имеет специальное значение. Проще вынести сложное форматирование даты в отдельный скрипт.
  6. Заполнен диск. Проверьте блоки и inode: задача может запускаться и не иметь возможности записать результат.

Журнал самой службы смотрят через journalctl -u cron --since today. На системах с соответствующей настройкой syslog записи CRON также находятся в /var/log/syslog.

После завершения опыта удалите учебную строку из crontab -e, сохраните остальные задания и убедитесь, что новая запись больше не появляется. Рабочее расписание стоит считать готовым только после нескольких успешных запусков и проверки результата, ради которого оно создано.

Первоисточники

Нашли неточность?

Сообщите адрес статьи и фрагмент через контакты редакции.