Настройка cron: как запускать задачи по расписанию

Задачи по расписанию: настройка cron с нуля до бэкапа

Марина
Марина
📅 29 сентября 2026
Задачи по расписанию: настройка cron с нуля до бэкапа

Cron — это встроенный в Linux планировщик: он раз в минуту просыпается, сверяет текущее время со списком заданий и запускает те, чьё расписание совпало. Настройка сводится к одной строке из пяти полей времени и команды, которую нужно выполнить, а работает такая строка одинаково и на домашнем компьютере, и на арендованном VPS с Linux. Никакого отдельного демона (так называют фоновую программу, которая работает без вашего участия) ставить не нужно: пакет cron уже стоит в Ubuntu, Debian, CentOS и Alma Linux.

Материал для тех, кто первый раз открывает crontab и хочет, чтобы бэкап реально делался каждую ночь, а не «делался» только в голове. Разберём формат расписания, шаблоны на каждые 15 минут и на воскресенье, отличие пользовательского задания от системного, три классические грабли (окружение, потерянный вывод, наложение запусков) и два готовых примера — ночной архив сайта с базой и автопродление сертификата. В конце — способы проверить, что задание действительно отработало.

Как устроен cron и чем он отличается от systemd-таймеров

Демон cron стартует вместе с системой и держит в памяти таблицу заданий. Каждую минуту он сравнивает системное время с расписанием каждой строки и, если совпадение есть, запускает команду в отдельной оболочке от имени владельца задания. Минимальный интервал в классическом cron — 1 минута: запустить что-то раз в 10 секунд напрямую не получится.

Альтернатива — systemd-таймеры: расписание описывается двумя файлами (unit и timer), зато есть точность до секунды и опция запуска пропущенных заданий после перезагрузки. Cron проигрывает в гибкости, но выигрывает в скорости настройки: одна строка вместо двух файлов. Для 90% бытовых задач — бэкап, чистка временных файлов, обновление сертификата — этого достаточно.

Ещё одна особенность: cron не «догоняет» пропущенное. Если сервер был выключен в 3:00, ночное задание не выполнится, и следующая попытка будет через 24 часа. Для машин, которые регулярно уходят в перезагрузку, существует anacron — отдельный планировщик, который ориентируется на факт «прошли сутки с последнего запуска», а не на время на часах.

Пять полей расписания: как читать строку crontab

Строка задания состоит из пяти полей времени и команды. Порядок полей всегда один: минута, час, день месяца, месяц, день недели. Диапазоны такие: минуты 0–59, часы 0–23, дни месяца 1–31, месяцы 1–12, дни недели 0–7, где и 0, и 7 означают воскресенье.

Звёздочка в поле означает «любое значение». Строка * * * * * — это «каждую минуту каждого часа каждого дня». Косая черта задаёт шаг: */15 в поле минут — это 0, 15, 30 и 45, то есть четыре запуска в час. Запятая перечисляет значения: 0,30 — ровно в начале часа и на 30-й минуте. Дефис задаёт диапазон: 9-18 в поле часов — это десять запусков с 9 до 18 включительно.

Важная тонкость: если заполнены одновременно поле «день месяца» и поле «день недели», cron считает их через ИЛИ, а не через И. Строка 0 5 13 * 5 сработает и 13-го числа любого месяца, и каждую пятницу. Чтобы избежать путаницы, в одном из этих двух полей почти всегда ставят звёздочку.

Готовые шаблоны расписаний на каждый день

Проще всего начать с готовой строки и подправить цифры под себя. В таблице ниже — расписания, которые закрывают большинство задач на сервере, с указанием, сколько запусков в сутки они дают. Последняя колонка помогает прикинуть нагрузку: задание раз в минуту за сутки стартует 1440 раз, и если каждый запуск читает базу, это уже заметно.

Кроме числовой записи cron понимает сокращения-псевдонимы: @daily (равносильно 0 0 * * *), @hourly (0 * * * *), @weekly, @monthly и @reboot — последний запускает команду один раз после старта системы. Псевдонимы читаются легче, но не позволяют выбрать конкретный час, поэтому в реальных заданиях чаще пишут цифрами.

Строка Что означает Запусков в сутки
0 3 * * * Каждый день в 3:00 1
*/15 * * * * Каждые 15 минут 96
0 4 * * 0 По воскресеньям в 4:00 1 раз в неделю
30 2 1 * * 1-го числа каждого месяца в 2:30 1 раз в месяц
0 9-18 * * 1-5 Ежечасно с 9 до 18 в будни 10 в рабочий день
*/5 * * * * Каждые 5 минут 288
* * * * * Каждую минуту 1440
Пять полей расписания crontab и шаблоны с числом запусков в сутки
Число запусков стоит прикидывать заранее: строка раз в минуту стартует 1440 раз за сутки, и каждое обращение к базе умножается на это.

Где лежат задания: пользовательский crontab и системные файлы

Свой список заданий каждый пользователь редактирует командой crontab -e — она открывает временный файл в редакторе по умолчанию и после сохранения проверяет синтаксис. Посмотреть текущее содержимое без редактора — crontab -l. Удалить всё разом — crontab -r. Это единственная из трёх команд, которая ничего не переспрашивает: список исчезнет сразу и без подтверждения. Поэтому перед ней полезно сохранить копию: crontab -l > ~/cron-backup.txt запишет текущие задания в файл в домашнем каталоге.

Задания разных пользователей не пересекаются. То, что вы завели под обычным логином, выполнится от его имени и не получит прав на системные каталоги. Чтобы задание работало от суперпользователя, его заводят через sudo crontab -e. Подробнее про менеджер crontab и его роль в панелях управления — в глоссарии.

Второй способ — системные файлы в каталоге /etc/cron.d. Формат там почти тот же, но между расписанием и командой добавляется шестое поле — имя пользователя, от которого запускать. Такой файл удобно класть вместе с приложением: он лежит в системе как обычный конфиг, попадает в резервную копию сервера и виден любому администратору. Ещё есть каталоги /etc/cron.daily и /etc/cron.weekly — туда достаточно положить исполняемый скрипт без всякого расписания: система сама запустит его раз в сутки или раз в неделю.

# Пользовательское задание: пять полей + команда
0 3 * * * /usr/local/bin/backup.sh

# Системный файл /etc/cron.d/backup: шестое поле (root) — имя пользователя
0 3 * * * root /usr/local/bin/backup.sh

Главная грабля: короткий PATH и чужой рабочий каталог

Девять из десяти историй «в терминале работает, из cron — нет» упираются в окружение. Когда вы заходите по SSH, оболочка читает профиль пользователя и подставляет переменную PATH — список каталогов, где искать программы. Cron профиль не читает: у него свой короткий PATH, обычно /usr/bin:/bin. Поэтому команда вроде docker, certbot или python3.11 из /usr/local/bin просто не находится, и задание падает с «command not found».

Второе отличие — рабочий каталог. Cron стартует команду из домашнего каталога владельца задания, а не из папки проекта. Любой относительный путь вида ./script.sh или backup/db.sql будет считаться относительно домашнего каталога и укажет не туда, куда вы думали.

Лечится это тремя приёмами. Первый: писать в задании только абсолютные пути и к программе, и к файлам. Второй: задать PATH прямо в верхней строке crontab — переменные, объявленные до заданий, действуют на все строки ниже. Третий: если скрипту нужен конкретный каталог, явно перейти в него через cd в самой команде.

# Переменные объявляем до заданий — они действуют на все строки ниже
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash

# Абсолютные пути и явный переход в каталог проекта
15 2 * * * cd /var/www/site && /usr/bin/php artisan schedule:run

Куда пропадает вывод задания и как завести нормальный лог

По умолчанию всё, что команда напечатала в стандартный вывод или в поток ошибок, cron отправляет письмом локальному пользователю. Если на сервере не настроен почтовый агент (программа, которая эти письма отправляет), а на свежем VPS его обычно нет, письмо никуда не уходит и вывод теряется. Внешне это выглядит так: задание «молча не работает», и понять причину невозможно.

Решение — перенаправить оба потока в файл. Конструкция >> /var/log/job.log дописывает стандартный вывод в конец файла, а 2>&1 добавляет туда же поток ошибок. Порядок важен: если поставить 2>&1 перед >>, ошибки уйдут не в файл, а туда, куда вывод шёл раньше, — то есть снова потеряются. Одна стрелка > вместо двух перезапишет файл при каждом запуске, и вы увидите только последний прогон.

Чтобы лог не рос бесконечно, к нему подключают ротацию через logrotate: конфиг в /etc/logrotate.d/ с хранением 14 архивов и сжатием держит размер в пределах нескольких мегабайт. Полезная привычка — печатать в начале каждого запуска дату, тогда по логу видно не только ошибки, но и сам факт старта.

# Весь вывод и все ошибки — в один файл, дописыванием
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

# С отметкой времени в начале строки
0 3 * * * date "+%F %T старт" >> /var/log/backup.log; /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Блокировка одновременных запусков через flock

Задание раз в 5 минут обычно отрабатывает за секунды. Но однажды база вырастет, диск затормозит, и прошлый запуск не успеет закончиться до следующего. Cron это не отслеживает: он честно запустит вторую копию, потом третью, и через час на сервере будет десяток одновременных процессов, которые дерутся за диск. Load average — средняя очередь задач к процессору и диску — уходит в потолок, и сайт начинает отдавать ошибку 502.

Штатное лекарство — утилита flock из пакета util-linux, она есть почти в любом дистрибутиве. Команда берёт «замок» на указанный файл и держит его до завершения процесса. Флаг -n означает «не ждать»: если замок уже занят предыдущим запуском, новая копия молча завершится с кодом 1 и не наделает дублей.

Есть и вариант с ожиданием: -w 60 заставит новый процесс подождать освобождения замка до 60 секунд. Такой режим уместен для коротких задач. Для тяжёлых бэкапов лучше жёсткий -n: пропустить один запуск дешевле, чем получить два параллельных архива одной базы.

# -n: если предыдущий запуск ещё идёт, просто выходим
*/5 * * * * /usr/bin/flock -n /tmp/sync.lock /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1

# -w 60: подождать освобождения замка не больше 60 секунд
*/5 * * * * /usr/bin/flock -w 60 /tmp/sync.lock /usr/local/bin/sync.sh
Расписание cron с блокировкой flock и проверка запуска в журнале
Без перенаправления вывод уходит письмом на локальный ящик и теряется, поэтому задание выглядит молча не работающим.

Пример первый: ночной архив сайта и базы

Соберём практичное задание: каждую ночь упаковать каталог сайта и дамп базы MySQL в один архив, а копии старше 14 дней удалить. Сначала пишем скрипт, потом ставим его в расписание — так проще отлаживать и не придётся экранировать знаки процента, которые cron трактует как перевод строки.

  1. Создайте каталог для копий: sudo mkdir -p /var/backups/site и убедитесь, что на диске свободно хотя бы вдвое больше, чем весит проект.
  2. Положите пароль базы в файл /root/.my.cnf с правами 600 (читать и писать может только владелец файла), чтобы пароль не светился в списке процессов при каждом запуске.
  3. Создайте файл /usr/local/bin/backup.sh с содержимым из блока ниже и сделайте его исполняемым: sudo chmod +x /usr/local/bin/backup.sh.
  4. Запустите скрипт руками — sudo /usr/local/bin/backup.sh — и проверьте, что архив появился и открывается.
  5. Добавьте строку в системный crontab через sudo crontab -e и сохраните файл.
  6. На следующее утро посмотрите лог: tail -n 30 /var/log/backup.log.
#!/bin/bash
set -e
DATE=$(date +%F)
tar -czf /var/backups/site/files-$DATE.tar.gz /var/www/site
mysqldump --single-transaction mydb | gzip > /var/backups/site/db-$DATE.sql.gz
find /var/backups/site -type f -mtime +14 -delete
# Каждый день в 3:10, с замком и логом
10 3 * * * /usr/bin/flock -n /tmp/backup.lock /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Строка set -e останавливает скрипт на первой же ошибке, иначе find удалит старые копии даже после неудачного архивирования. Ключ --single-transaction снимает дамп без блокировки таблиц, так что сайт продолжает работать. Локальные копии — только половина дела: их стоит отправлять на второй сервер или в объектное хранилище, для чего удобен rsync. Подробный разбор схем хранения — в отдельном гайде про настройку бэкапов на VPS, а базовая теория — в статье про резервное копирование.

Пример второй: автопродление TLS-сертификата

Сертификаты Let’s Encrypt живут 90 дней, продлевать их принято начиная с 30-го дня до окончания. Делать это руками четыре раза в год — верный способ однажды забыть и получить страницу с предупреждением браузера. Команда Certbot renew сама проверяет срок и обновляет только те сертификаты, которым осталось меньше 30 дней, поэтому её безопасно запускать хоть каждый день.

Хорошая практика — ставить два запуска в сутки в непохожие минуты, например в 3:27 и 15:27. Так продление не совпадёт с пиком обращений к серверам центра сертификации, а если первая попытка упрётся в сетевой сбой, вторая её подхватит. Флаг --quiet убирает болтовню в лог, оставляя только ошибки, а --deploy-hook перезагружает веб-сервер строго после успешного обновления.

# Две попытки в сутки, перезапуск nginx только при реальном обновлении
27 3,15 * * * /usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx" >> /var/log/certbot.log 2>&1

Проверить логику до боевого запуска помогает режим certbot renew --dry-run: он проходит весь сценарий на тестовом сервере и ничего не меняет. Если сертификат выпускался вручную и нужен разбор с нуля, пригодится пошаговая инструкция по установке SSL от Let’s Encrypt.

Как проверить, что задание действительно отработало

Диагностику удобно вести слоями: сначала выясняем, дошло ли дело до запуска, потом — что случилось с самой командой. Первый слой — журнал системы. Команда journalctl -u cron --since today покажет строки вида «CMD (…)» с точным временем старта каждого задания. На CentOS и Alma юнит называется crond, а в старых системах записи лежат в /var/log/syslog.

Если запись о старте есть, а результата нет — проблема внутри команды, и ответ ищем в собственном логе задания. Если записи нет вовсе, проверяем три вещи: жив ли сам демон (systemctl status cron), сохранилась ли строка (crontab -l) и верен ли часовой пояс (timedatectl). Классика: сервер стоит в UTC, задание на 3:00 отрабатывает в 6:00 по Москве.

Что проверяем Команда Плохой ответ
Демон запущен systemctl status cron inactive (dead)
Задание сохранено crontab -l Пустой вывод
Факт запуска journalctl -u cron —since today Нет строки CMD
Часовой пояс timedatectl Time zone: Etc/UTC
Права на скрипт ls -l /usr/local/bin/backup.sh Нет флага x
Свободное место df -h /var Use% выше 95%

Ещё один типичный сценарий — задание стартовало, но было убито системой из-за нехватки памяти. Тогда в dmesg найдётся строка от OOM-killer с именем процесса. Разобраться, кто именно съел память и процессор, помогает разбор команд из материала о том, как смотреть процессы в Linux.

На каком сервере держать задачи по расписанию

Требования у cron скромные: сам демон занимает единицы мегабайт памяти. Реальные аппетиты определяет команда, которую вы запускаете. Архивирование каталога на 5 ГБ спокойно проходит на 1 ГБ оперативной памяти, а вот mysqldump большой базы или сборка проекта потребуют запаса — и по памяти, и по свободному месту под временные файлы.

Ориентир по ресурсам простой: диска нужно минимум вдвое больше, чем весит то, что вы архивируете, плюс место под 7–14 старых копий. Если бэкапы уезжают на внешнее хранилище в ту же ночь, хватит и полуторного запаса. Ниже — стартовые конфигурации площадок каталога, на которых такие задачи держат чаще всего.

Провайдер Стартовая цена Что входит
Cloudcore от 59 ₽/мес 1 ядро, 1 ГБ RAM, 10 ГБ NVMe
Hosting-Russia от 99 ₽/мес Стартовый тариф под мелкие задачи
4VPS от 110 ₽/мес 1 ядро, 1 ГБ RAM, 5 ГБ NVMe
HostVDS от 127 ₽/мес 1 ядро, 1 ГБ RAM, 10 ГБ NVMe

Данные проверены 04.09.2026.

Всего в каталоге 44 площадки, и разброс стартовых цен — от 59 до 593 ₽/мес. Для машины, которая только гоняет скрипты по расписанию, переплачивать смысла нет: подойдёт любой тариф из подборки недорогих VPS. Единственное, на что стоит смотреть, — объём диска под архивы и наличие снапшотов.

Частые ошибки

Ошибки в cron почти всегда однотипные, и большинство из них видно ещё до первого ночного запуска. Ниже — те, что чаще всего доводят до вопроса «почему задание не сработало». Проверьте свою строку по списку сразу после сохранения.

  • Относительные пути вместо абсолютных — cron стартует из домашнего каталога, а не из папки проекта.
  • Неэкранированный знак процента: в crontab он означает перевод строки, поэтому в командах с date его пишут как %.
  • Файл crontab без пустой строки в конце — часть систем молча игнорирует последнее задание.
  • Скрипт без флага исполнения или без строки #!/bin/bash в первой строке файла.
  • Задание раз в минуту без flock — рано или поздно запуски наложатся друг на друга.
  • Часовой пояс сервера в UTC, а расписание составлено по московскому времени.

Частые вопросы

Как запустить задание чаще, чем раз в минуту? Штатными средствами cron — никак, его минимальный шаг составляет 1 минуту. Обходной путь — одна строка раз в минуту, внутри которой скрипт делает нужное действие несколько раз с паузой sleep 20. Для честных секундных интервалов лучше взять systemd-таймер с параметром OnUnitActiveSec — он задаёт паузу между запусками в секундах.

Почему команда работает в терминале, но не работает из cron? В 90% случаев виноват PATH: cron даёт короткий список каталогов и не находит программу из /usr/local/bin. Пропишите полный путь к бинарнику или объявите PATH в первой строке crontab. Второй по частоте виновник — относительные пути к файлам.

Куда уходит вывод, если не настроить перенаправление? Cron пытается отправить его письмом на локальный ящик владельца задания. Без настроенного почтового агента письмо теряется, и вы не увидите ни результата, ни ошибки. Всегда добавляйте >> /var/log/имя.log 2>&1.

Что будет, если сервер был выключен во время запуска? Задание просто пропустится, cron не выполняет пропущенное после включения. Если сервер регулярно перезагружается, используйте anacron — он отсчитывает интервал от последнего фактического запуска, а не от времени на часах.

Как временно отключить задание, не удаляя его? Поставьте символ # в начале строки через crontab -e — строка станет комментарием и перестанет выполняться. Это удобнее, чем удалять: сохраняется и расписание, и точная команда со всеми ключами.

Нужно ли перезапускать cron после правки расписания? Нет, если вы редактировали через crontab -e — демон подхватывает изменения сам. Перезагрузка через systemctl reload cron нужна только после ручной правки файлов в /etc/cron.d на некоторых системах.

Можно ли ставить задачи по расписанию на обычном виртуальном хостинге? Обычно да, но с ограничениями: панель часто разрешает минимальный интервал 5 или 15 минут и запрещает произвольные системные команды. Полная свобода в расписании и в наборе утилит появляется только на VPS с root-доступом.

Итог

Настройка cron сводится к трём правилам: абсолютные пути в команде, перенаправление вывода в свой лог-файл и flock -n на всём, что запускается чаще раза в час. Сохраните расписание через crontab -e, проверьте факт запуска в journalctl -u cron, и через сутки у вас будет и рабочий бэкап, и понятный лог. Всё это требует root-доступа, который даёт VPS на Linux — стартовые тарифы в каталоге начинаются от 59 ₽/мес.

Поделиться:
👁 33 просмотров
Марина
Редактор · FREEHOSTING
Главный редактор FREEHOSTING. С 2020 года тестирует VDS, VPS и хостинг-провайдеров — арендует серверы, нагружает их реальными проектами и пишет честные обзоры по итогам. Помогает читателям выбирать хостинг под свои задачи: от Telegram-бота до production-сайта.