Back to Blog Field Notes
2 min read ✉ Subscribe
The CLI php.ini is not the FPM php.ini
Field Notes

The CLI php.ini is not the FPM php.ini

An hour lost to a two-megabyte upload limit that did not exist, because the value came from the wrong SAPI.

AX
Alien XP
Jul 22, 2026 · 2 min read

This one is embarrassing in retrospect and costs everybody an hour exactly once. Uploads were failing on a site, so the obvious first check:

$ php -r 'echo ini_get("upload_max_filesize");'
2M

There it is. Two megabytes, the ancient PHP default, obviously the problem. Raise it, restart, move on. Except uploads much larger than 2 MB had been working for months, which should have been the clue.

Two configurations, one binary

PHP reads a different config directory for each SAPI. On a Debian-family system:

/etc/php/8.3/cli/php.ini      + cli/conf.d/*.ini      <- php on the command line
/etc/php/8.3/fpm/php.ini      + fpm/conf.d/*.ini      <- everything served over HTTP
/etc/php/8.3/apache2/php.ini  + apache2/conf.d/*.ini   <- mod_php, if you use it

Your website runs under FPM. php -r on the command line runs under CLI. They can differ by a factor of a thousand and nothing will warn you, because both are correct — they are answering about different runtimes.

Ask the runtime that actually serves the request

Drop a file in the docroot, fetch it over HTTP, delete it:

printf '<?php echo ini_get("upload_max_filesize");' > /var/www/site/_ini.php
curl -s https://example.com/_ini.php
rm /var/www/site/_ini.php

On the server in question that returned 2048M. The limit I had spent an hour on did not exist. The upload failures were something else entirely.

The general rule: reproduce the behaviour through the same path the user takes. A shell is not that path. This is the same reason you test a form submission with curl -X POST against the real endpoint rather than calling the handler from a script — the sandbox, the user, the environment and the config all differ.

The second mistake, which was worse

Having decided the limit was wrong, I ran a helper that writes PHP settings — and it replaced the file rather than merging into it. Six settings that had been configured deliberately were gone, silently, because the tool took a list of keys and wrote exactly those.

Two lessons worth more than the hour: read a config file before you write it, and when a tool takes "the settings", find out whether it means "these settings" or "all settings". Nothing in the output distinguishes the two until you diff the file afterwards.

# before touching anything
cp /etc/php/8.3/fpm/conf.d/99-local.ini{,.bak}
# after
diff /etc/php/8.3/fpm/conf.d/99-local.ini{.bak,}
AX

Alien XP

Field Notes · 10 essays

Runs the servers behind AlphaPanel and writes up what breaks.

Keep reading

All posts