Чем PHP CLI отличается от PHP-FPM для cron-задач
PHP-FPM обслуживает HTTP-запросы через Nginx и работает в режиме пула воркеров с общим php.ini и лимитами, заданными для веб-нагрузки. PHP CLI — это отдельный бинарник /usr/bin/php8.3, который запускается из командной строки или из crontab и читает свой собственный конфиг, обычно из /etc/php/8.3/cli/php.ini. Значения memory_limit, max_execution_time и error_reporting в CLI и FPM могут отличаться, и это частая причина, почему скрипт в браузере работает, а тот же скрипт по расписанию падает или наоборот.
Второе отличие — окружение. У процесса cron нет переменных PATH, HOME и локали, которые есть у интерактивной оболочки, и нет рабочего каталога по умолчанию: он всегда стартует из домашнего каталога пользователя, от которого запущен cron.
Как правильно прописать задачу в crontab
Правило crontab должно указывать полный путь к бинарнику PHP, полный путь к скрипту и рабочий каталог, если скрипт использует относительные пути к файлам.
*/5 * * * * cd /var/www/app && /usr/bin/php8.3 artisan schedule:run >> /var/log/app/cron.log 2>&1
Символ && гарантирует, что php запустится только после успешного перехода в каталог, а 2>&1 перенаправляет ошибки в тот же лог-файл, что и обычный вывод. Без этого фатальные ошибки PHP молча исчезают: cron отправляет их на локальную почту, которую никто не читает.
Своя версия php.ini для CLI
Проверить, какой конфиг реально использует CLI, можно одной командой — она покажет путь к файлу, а не предположение.
php --ini
php -i | grep memory_limit
Если на сервере установлено несколько версий PHP, как описано в статье о нескольких версиях PHP на одном сервере, в crontab обязательно указывайте версию явно: /usr/bin/php8.3, а не просто php — иначе задача может внезапно выполниться под другой версией после обновления alternatives.
Память, таймауты и рабочий каталог: частые грабли
| Параметр | Веб (FPM) | Cron (CLI) |
|---|---|---|
| memory_limit | 128M | часто без ограничения (-1) |
| max_execution_time | 30 секунд | 0, ограничения нет |
| рабочий каталог | корень сайта | домашний каталог пользователя |
| error_log | лог PHP-FPM | stderr, если не задан явно |
Значения из таблицы задаются в разных секциях php.ini: [PHP] влияет на оба режима, а блок cli/conf.d — только на CLI. Управление памятью и загрузкой подробно разобрано в статье про memory_limit и upload_max_filesize.
Логирование вывода cron-задач
- Всегда перенаправляйте stdout и stderr в отдельный файл лога, а не полагайтесь на почту cron.
- Указывайте display_errors = Off и log_errors = On в cli/php.ini — вывод ошибок в консоль ломает формат, если скрипт печатает JSON или CSV.
- Ротацию log-файла cron настраивайте через logrotate, иначе файл вырастет до гигабайтов за месяц.
- Разбор упавших задач начинайте с error_log, а не с догадок — принципы разобраны в статье про логи PHP и разбор фаталов.
- Добавляйте в начало каждой задачи date и hostname в лог — на кластере из нескольких серверов иначе не понять, кто именно писал в лог.
Итог: чек-лист для PHP-задач в cron
- В crontab указан полный путь к бинарнику нужной версии PHP и рабочий каталог через cd.
- Вывод команды перенаправлен в файл лога с ротацией через logrotate.
- Проверены memory_limit и max_execution_time именно в cli/php.ini, а не в конфиге FPM.
- display_errors выключен, log_errors включён, ошибки идут в файл, а не в почту root.
- Задача проверена вручную от имени того же пользователя, от которого её запускает cron.