
Как заменить WP-Cron системным расписанием
Отключаем запуск cron при посещениях, создаём systemd timer, выполняем только просроченные события WordPress и проверяем журнал без дублей.
Содержание
WP-Cron хранит расписание внутри WordPress, но по умолчанию проверяет его во время веб-запросов. На редко посещаемом сайте публикация и очистка кеша могут опоздать. На загруженном сайте многочисленные запросы создают лишние попытки запустить одну очередь.
Системный timer решает только вопрос времени запуска. Сами события, их повторы и обработчики по-прежнему принадлежат WordPress и плагинам. Перед изменением нужно увидеть текущую очередь и убедиться, что один проход завершается быстрее выбранного интервала.
Проверьте очередь и ручной запуск
Инструкция использует WP-CLI. Запускайте его под тем же непривилегированным пользователем, который имеет необходимые права к WordPress, и всегда указывайте путь сайта:
sudo -u www-data /usr/local/bin/wp \
--path=/srv/www/example.com/current \
cron event list --fields=hook,next_run_relative,recurrence --format=table
В списке найдите события с большим опозданием, неизвестные хуки и очень частое расписание. Хук — имя события, по которому WordPress вызывает зарегистрированный обработчик. Не удаляйте его, пока не установите, какому плагину он принадлежит.
Теперь выполните готовые события вручную и измерьте время. Эта команда может отправлять письма, публиковать записи и очищать кеш, поэтому проводите тест осознанно:
time sudo -u www-data /usr/local/bin/wp \
--path=/srv/www/example.com/current \
cron event run --due-now
Ожидается отчёт об успешно выполненных событиях и код выхода 0. Если команда зависает или падает, сначала исправьте конкретный обработчик. Автоматизация не сделает неисправное событие надёжным.
Создайте отдельную systemd-службу
Service unit описывает одно выполнение команды. Создайте /etc/systemd/system/wordpress-cron.service со следующим содержимым, заменив путь и пользователя:
[Unit]
Description=Run due WordPress cron events
After=network-online.target mariadb.service
Wants=network-online.target
[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/wp --path=/srv/www/example.com/current cron event run --due-now
Nice=10
Type=oneshot означает один запуск без постоянно работающего процесса. Зависимость от сети и MariaDB задаёт разумный порядок загрузки, но не гарантирует доступность внешнего SMTP или API. Пользователь не должен быть root.
Загрузите unit и выполните его один раз вручную. Сразу прочитайте состояние и журнал:
sudo systemctl daemon-reload
sudo systemctl start wordpress-cron.service
systemctl status wordpress-cron.service --no-pager
journalctl -u wordpress-cron.service -b --no-pager -n 100
Для oneshot состояние inactive (dead) после успешного завершения нормально; важны status=0/SUCCESS и сообщения WP-CLI. При ошибке исправьте путь, права или событие до создания расписания.
Добавьте timer и защиту от пропуска
Создайте /etc/systemd/system/wordpress-cron.timer. Пример запускает задачу через две минуты после загрузки и затем каждые пять минут:
[Unit]
Description=Schedule WordPress cron events
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
Persistent=true
Unit=wordpress-cron.service
[Install]
WantedBy=timers.target
Persistent=true запускает пропущенную задачу после следующей загрузки. Это не создаёт отдельный запуск за каждый пропущенный интервал: WordPress обработает события, срок которых наступил. Если проход длится дольше пяти минут, systemd не запустит второй экземпляр уже активной службы, но очередь будет отставать.
Включите timer и проверьте вычисленное время следующего запуска:
sudo systemctl enable --now wordpress-cron.timer
systemctl list-timers wordpress-cron.timer --all
systemctl status wordpress-cron.timer --no-pager
Подождите один интервал и снова прочитайте журнал service. Только после успешного автоматического запуска переходите к отключению веб-триггера.
Отключите запуск при посещениях
Добавьте в wp-config.php до завершающего комментария одну константу. Она отключает только запуск по веб-запросу; созданные события и их расписание останутся в базе:
define( 'DISABLE_WP_CRON', true );
Проверьте синтаксис PHP и откройте сайт. Константа запрещает WordPress инициировать cron из веб-запроса, но не удаляет расписание и не мешает WP-CLI выполнять события.
Если timer перестал работать, временный возврат прост: закомментируйте константу, проверьте сайт и отключите неисправный timer командой sudo systemctl disable --now wordpress-cron.timer. Затем исправляйте unit без остановки фоновых задач.
Наблюдайте не только за зелёным статусом
Раз в несколько дней проверяйте просроченные события, длительность прохода и размер журнала. Успешный код WP-CLI не всегда подтверждает доставку письма или завершение внешнего API-запроса — для таких действий нужен журнал соответствующего сервиса.
После установки нового плагина сравнивайте очередь: он может добавить ежеминутную тяжёлую задачу. Правила выбора расширений описаны в предыдущей главе, а следующий внешний слой — доставка служебной почты.
Источники
Самопроверка
Проверьте, что материал усвоен
Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.
Разбираем коротко
Частые вопросы
Почему WP-Cron может опаздывать?
По умолчанию проверка событий связана с посещениями. Если запросов нет, запуск может задержаться; при большом трафике появляются лишние попытки запуска.
Как часто запускать системный таймер?
Интервал должен соответствовать самым частым нужным событиям и длительности выполнения. Для обычного сайта разумно начать с нескольких минут и проверить фактическую очередь.
Нужно ли оставлять WP-Cron включённым после настройки timer?
Нет. После успешной проверки системного запуска добавьте DISABLE_WP_CRON, иначе останутся два источника запуска.


