We may earn a referral fee at no additional cost to you when you shop through the links on this page.
Your PHP-FPM app is throwing 500s and the log file is empty. This DevAnswers walkthrough covers where Nginx and PHP-FPM write logs by default on Ubuntu and Debian, why errors don’t always appear, and how to read them via tail, journalctl, or docker logs.
Default Log Locations on Linux
Nginx maintains two log files by default on every supported Linux distribution:
- Error log:
/var/log/nginx/error.log; server errors, configuration problems, and PHP-FPM errors that bubble up viafastcgi_pass. - Access log:
/var/log/nginx/access.log; every HTTP request including status codes, client IPs, and user agents.
PHP-FPM is more variable. The exact log path depends on which PHP version your distribution packages. The default php-fpm metapackage on each release pulls in a different version-pinned package, and that package writes to a version-stamped log file:
| Distribution | PHP version | Default FPM log file |
|---|---|---|
| Ubuntu 22.04 (Jammy) | 8.1 | /var/log/php8.1-fpm.log |
| Ubuntu 24.04 (Noble) | 8.3 | /var/log/php8.3-fpm.log |
| Ubuntu 26.04 (Resolute) | 8.5 | /var/log/php8.5-fpm.log |
| Debian 12 (Bookworm) | 8.2 | /var/log/php8.2-fpm.log |
| Debian 13 (Trixie) | 8.4 | /var/log/php8.4-fpm.log |
To check what’s running on your server, look up the metapackage version:
apt-cache policy php-fpm
The FPM master log records pool start/stop events, slow-script reports, and segfaults. It does not automatically include every PHP error your application throws. Those usually surface in Nginx’s error log via fastcgi_pass, or they end up captured by systemd’s journal. The next two sections explain why.
Why Your PHP Errors Aren’t Appearing
This is the most common reason readers land here: PHP-FPM is running, the app is throwing errors, but tail /var/log/nginx/error.log shows nothing. Six things are usually responsible.
log_errorsis off. PHP’sdisplay_errorsdirective controls whether errors render in the browser response;log_errorsindependently controls whether they go to a log file. Productionphp.inisetsdisplay_errors = Offfor security, which is correct, but iflog_errorsis alsoOffnothing gets written anywhere. See our guide to the PHP display_errors directive for the distinction in detail.- The pool’s
error_logis unset. PHP-FPM pools (/etc/php/<ver>/fpm/pool.d/www.conf) can declarephp_admin_value[error_log]. If they don’t, PHP-FPM falls back to the master FPM log or to stderr, neither of which may be where you’re looking. catch_workers_outputis disabled. By default, FPM worker stdout and stderr are sent to/dev/null. Settingcatch_workers_output = yesin the pool config redirects worker output into the FPM error log instead.- Some output lands in the systemd journal, not the log file. On Ubuntu 22.04 and later (and Debian 12+), php-fpm runs as a systemd service. The packaged default does write the master log to the file in the table above, but anything the process prints to stdout or stderr, like a config error that kills the service at startup, lands in journald instead. The same applies whenever
error_logpoints at/proc/self/fd/2(the Docker default) or is unset.tailwon’t find those entries;journalctlwill. Covered in the Reading PHP-FPM Logs with journalctl section below. - File permissions block the writer. The PHP-FPM master runs as
rootbut workers drop towww-data. If the pool’serror_logpath or its parent directory aren’t writable bywww-data, the error is silently dropped. Verify ownership withls -la /var/log/. - Errors are flowing to Nginx but you’re tailing the wrong file. Nginx’s
error_logdirective can appear in thehttp,server, orlocationblock of your config. A per-server-blockerror_log /var/log/nginx/site-a.log;sends one site’s errors to a different file. Runsudo nginx -Tto dump the full effective config and grep forerror_log.
sudo nginx -T | grep -E '^\s*error_log' to see every error_log directive Nginx actually loaded, in include order. Run it with sudo: unprivileged, the dump aborts on root-only files such as TLS keys and silently comes back incomplete. Since nginx 1.5.2, multiple error_log directives in the same scope all receive log lines; the last one does not override the others. Configuring PHP-FPM to Log Its Own Errors
Once you know which directives matter, fixing this is mechanical. Open the default pool config (replace the version with whatever your distribution ships):
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
Set or uncomment the following directives in the [www] pool. The two php_admin_* lines force-set per-pool values that override anything in php.ini; catch_workers_output ensures worker stdout/stderr is captured rather than discarded:
; Where this pool writes its errors
php_admin_value[error_log] = /var/log/php8.3-fpm/devans.log
; Make sure errors are logged at all
php_admin_flag[log_errors] = on
; Capture worker stdout and stderr into the error log
catch_workers_output = yes
Save and exit (press CTRL + X, press Y, then press ENTER)
Create the directory and ensure the www-data user owns it before reloading:
sudo mkdir -p /var/log/php8.3-fpm
sudo chown www-data:www-data /var/log/php8.3-fpm
sudo systemctl reload php8.3-fpm
If you’re not sure whether reload picked up the change versus a full restart, see our notes on reloading PHP-FPM config without dropping connections. The directive locations above assume the default Debian/Ubuntu layout; if your php.ini lives somewhere unusual (manual builds, custom prefixes), our guide to finding and editing php.ini covers locating it across distros.
php_value / php_flag are mutable from user code via ini_set(); php_admin_value / php_admin_flag are not. Use the admin variants for production logging settings so applications cannot accidentally turn them off. Reading PHP-FPM Logs with journalctl
On Ubuntu 22.04+ and Debian 12+, php-fpm runs as a systemd unit. Per the systemd.exec man page, when a service doesn’t set StandardOutput= or StandardError=, those streams default to journal. On a stock install the master log still goes to the file from the table above, but startup failures printed to stderr, and any setup where error_log targets /proc/self/fd/2, end up in journald rather than a file under /var/log/.
Tail the journal for the FPM unit (replace the version number with whatever’s installed):
sudo journalctl -u php8.3-fpm.service -n 200
To watch new entries as they’re written:
sudo journalctl -u php8.3-fpm.service -f
Filter by time window, severity, or PID with the standard journalctl flags: --since "10 minutes ago", --priority err, or _PID=12345. If you’d rather force FPM logs into a file (so other tooling like logrotate or log shippers can grab them), set php_admin_value[error_log] in the pool config as shown in the previous section. Once that’s set, the journal stops being the source of truth for PHP errors.
Common Log Entries and What They Mean
Real-world Nginx and PHP-FPM error log lines tend to fall into a few recurring patterns. Recognising them shortcuts triage:
- 502 Bad Gateway: PHP-FPM isn’t running, crashed, or its socket/port doesn’t match what Nginx is configured to forward to. Check
systemctl statuson the FPM unit and confirm the socket path in the Nginxfastcgi_passdirective. - “connect() to unix:/run/php/php8.3-fpm.sock failed (13: Permission denied)”: Nginx’s worker user can’t read the PHP-FPM socket. Usually fixed by aligning the FPM pool’s
listen.owner/listen.groupwith Nginx’s user. - “upstream prematurely closed connection”: The PHP-FPM worker died mid-request, killed by
request_terminate_timeoutin the FPM pool config, a fatal error, or a segfault. (If Nginx gives up waiting pastfastcgi_read_timeout, the message is “upstream timed out” instead.) Check the FPM log for the kill, then raise the limit or fix the slow code. - “upstream sent too big header while reading response header from upstream”: PHP is sending headers larger than Nginx’s FastCGI buffers. Increase
fastcgi_buffer_sizeandfastcgi_buffers; this often surfaces with apps that send oversized cookies. - “PHP message: PHP Warning…” inside Nginx’s error log: PHP-FPM successfully forwarded a PHP-level warning. The log line is right; your application code is the issue.
Enabling Verbose / Debug Logging
If the default severity isn’t surfacing what you need, both Nginx and PHP-FPM accept a per-service severity level. Bump them temporarily during a debugging session, then revert.
Nginx’s error_log directive accepts eight severity levels in increasing order: debug, info, notice, warn, error, crit, alert, emerg. Setting a level captures that level and everything more severe; the default is error. Add debug only when you genuinely need full request tracing:
http {
# ...
error_log /var/log/nginx/error.log debug;
# ...
}
Save and exit (press CTRL + X, press Y, then press ENTER)
Reload Nginx after editing:
sudo systemctl reload nginx
PHP-FPM has its own log_level directive in php-fpm.conf with five valid values: alert, error, warning, notice, debug. The default is notice. To raise it:
; Possible values: alert, error, warning, notice, debug
log_level = debug
Save and exit (press CTRL + X, press Y, then press ENTER)
Then restart PHP-FPM:
sudo systemctl restart php8.3-fpm
error (Nginx) and notice (PHP-FPM) once you’ve finished investigating, and keep an eye on df -h while debug is enabled. PHP-FPM Logs in Docker Containers
The official php:fpm and php:fpm-alpine Docker images take a different approach. Containers don’t run systemd; they have one foreground process whose stdout and stderr the Docker daemon captures. To make PHP-FPM logs visible via docker logs, the official image redirects the FPM error log to the container’s stderr stream.
That’s done in /usr/local/etc/php-fpm.d/docker.conf shipped with the image (its companion zz-docker.conf only forces foreground mode). The relevant directive is:
[global]
error_log = /proc/self/fd/2
[www]
access.log = /proc/self/fd/2
catch_workers_output = yes
decorate_workers_output = no
By writing to /proc/self/fd/2, FPM is writing to its own file descriptor 2, which is the container’s stderr. Per Docker’s logging documentation, docker logs shows the container’s STDOUT and STDERR by default, and the json-file logging driver (Docker’s default) stores those streams as JSON files on local disk so the daemon can replay them on demand.
To watch logs from a running PHP-FPM container:
docker logs -f my-php-fpm-container
If you’ve built your own image starting from php:fpm and your application’s logs aren’t appearing, check that you haven’t accidentally removed the docker.conf overrides. A custom error_log = /var/log/php-fpm.log inside the container will write to a path that docker logs can never see.
Next Steps
For a deeper reference on Nginx’s two log files alone (formats, severity levels, custom log layouts), see our companion guide to Nginx error and access log locations. If you’re standing up a fresh server and want PHP-FPM correctly wired into Nginx from the start, work through our ultimate LEMP stack guide for Ubuntu 26.04; it covers the FPM pool config and Nginx fastcgi_pass integration end-to-end. Once your logs are flowing, logrotate handles disk pressure on its own; the relevant policies live under /etc/logrotate.d/nginx and the version-stamped FPM file (e.g. /etc/logrotate.d/php8.1-fpm on Ubuntu 22.04, /etc/logrotate.d/php8.3-fpm on Ubuntu 24.04, or /etc/logrotate.d/php8.4-fpm on Debian 13).
yes, it helped