# PHP в cron: чем CLI отличается от FPM на практике

> PHP в cron: разница между CLI и FPM, свой php.ini, память и таймауты, правильный crontab и логирование вывода задач.

## Чем 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 на одном сервере](https://www.zevshost.net/ru/kb/php-multi-version-linux/), в 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](https://www.zevshost.net/ru/kb/php-ini-memory-upload-limits/).

## Логирование вывода cron-задач

- Всегда перенаправляйте stdout и stderr в отдельный файл лога, а не полагайтесь на почту cron.
- Указывайте display\_errors = Off и log\_errors = On в cli/php.ini — вывод ошибок в консоль ломает формат, если скрипт печатает JSON или CSV.
- Ротацию log-файла cron настраивайте через logrotate, иначе файл вырастет до гигабайтов за месяц.
- Разбор упавших задач начинайте с error\_log, а не с догадок — принципы разобраны в статье [про логи PHP и разбор фаталов](https://www.zevshost.net/ru/kb/php-logi-oshibok-razbor/).
- Добавляйте в начало каждой задачи date и hostname в лог — на кластере из нескольких серверов иначе не понять, кто именно писал в лог.

## Итог: чек-лист для PHP-задач в cron

- В crontab указан полный путь к бинарнику нужной версии PHP и рабочий каталог через cd.
- Вывод команды перенаправлен в файл лога с ротацией через logrotate.
- Проверены memory\_limit и max\_execution\_time именно в cli/php.ini, а не в конфиге FPM.
- display\_errors выключен, log\_errors включён, ошибки идут в файл, а не в почту root.
- Задача проверена вручную от имени того же пользователя, от которого её запускает cron.

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

### Что будет, если в crontab не указать полный путь к бинарнику PHP?

Если указать просто php без полного пути, задача может выполниться под другой версией или вовсе не запуститься: у процесса cron нет переменной PATH интерактивной оболочки, и бинарник ищется совсем не там, где его находит обычный терминал пользователя. Поэтому в crontab всегда прописывают полный путь к бинарнику нужной версии, а не короткое имя команды.

### Можно ли увеличить memory\_limit только для одной cron-задачи, не трогая остальные?

Да, лимиты памяти и времени выполнения можно задать прямо в вызове, не трогая общий php.ini: ключи -d memory\_limit=... и -d max\_execution\_time=... передаются php как аргументы командной строки и действуют только для этого конкретного запуска. Такой подход удобнее правки общего конфига, если тяжёлая задача всего одна, а остальные скрипты должны работать с лимитами по умолчанию.

### Почему cron-задача, которая прекрасно работает вручную из терминала, падает по расписанию?

Обычно дело в разном окружении, а не в самом коде скрипта: в интерактивной оболочке заданы PATH, HOME и локаль, подключён привычный php.ini CLI с нужными настройками, а рабочий каталог — тот, из которого скрипт запустили руками. Процесс cron стартует без этого окружения и из домашнего каталога пользователя, поэтому относительные пути к файлам и переменные окружения нужно прописывать явно.

### Обязательно ли перенаправлять вывод cron-задачи в файл, если ошибок обычно не бывает?

Да, перенаправлять вывод стоит всегда, даже если задача обычно отрабатывает без ошибок. Без явного редиректа в файл фатальные ошибки PHP уходят в локальную почту cron, которую на сервере почти никто не читает, и первый же сбой останется незамеченным до жалобы пользователей. Лог с ротацией через logrotate — единственный надёжный способ увидеть, что именно пошло не так.

---

Источник: https://www.zevshost.net/ru/kb/php-cli-vs-fpm-cron/
