I can confirm the issue reported by @henri9813 and provide some additional investigation below with strace evidence.
Summary
After updating to nginx 2:1.20.1-28.el9_8.6.rocky.0.1, all PHP-FPM requests fail with HTTP 500 when SCRIPT_FILENAME uses $realpath_root and the document root goes through a symlink. This worked perfectly on the previous build (-el9_8.5).
The error log shows this alert on every request:
[alert] no buffer space in script copy
Environment
| Component |
Detail |
| OS |
Rocky Linux 9.8 |
| Stack |
nginx → php-fpm 8.3 via Unix socket |
| Docroot layout |
Capistrano-style symlink: /var/www/<app>/htdocs/current → /var/www/<app>/htdocs/releases/<ts> nginx root points to /var/www/<app>/htdocs/releases/<ts>/current/pub |
Package versions
working : 2:1.20.1-28.el9_8.5.rocky.0.1
broken : 2:1.20.1-28.el9_8.6.rocky.0.1
nginx configuration (simplified)
server {
server_name example.com;
root /var/www/<app>/htdocs/current/pub;
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/run/php-fpm/<app>.sock;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
}
How to reproduce
- Rocky 9.8 with
nginx 2:1.20.1-28.el9_8.6.rocky.0.1
- Any PHP app behind php-fpm, docroot reached through a symlink (
current → releases/<ts>), $realpath_root used in SCRIPT_FILENAME
- Run:
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: example.com' http://127.0.0.1/health_check.php
Actual result: HTTP 500 (nginx’s own default error page). Error log:
[alert] <pid>#<tid>: *2003 no buffer space in script copy, client: 127.0.0.1, server: example.com, request: "GET /health_check.php HTTP/1.1", host: "127.0.0.1"
Expected result: HTTP 200. The identical config worked on the same host with el9_8.5 until the package update; the alerts started immediately after.
Impact
- 100 % of PHP requests fail the application is completely down
- php-fpm itself is healthy; the failure happens entirely inside the nginx worker
Root cause analysis (strace of the nginx worker)
Failing case $realpath_root on el9_8.6
Key observation: nginx never connects to the FPM socket. The 500 is generated internally before the FastCGI request is even sent.
recvfrom(3, "GET /health_check.php HTTP/1.1\r\n"..., 1024, 0, NULL, NULL) = 70
newfstatat(AT_FDCWD, "/var/www/<app>/htdocs/current/pub/health_check.php", {st_mode=S_IFREG|0665, st_size=3481, ...}, 0) = 0
readlink("/var", 0x7fffec862410, 1023) = -1 EINVAL
readlink("/var/www", 0x7fffec862410, 1023) = -1 EINVAL
readlink("/var/www/<app>", 0x7fffec862410, 1023) = -1 EINVAL
readlink("/var/www/<app>/htdocs", 0x7fffec862410, 1023) = -1 EINVAL
readlink("/var/www/<app>/htdocs/current", "/var/www/<app>/htdocs/releases/<ts>"..., 1023) = 64
readlink("/var/www/<app>/htdocs/releases", 0x7fffec862410, 1023) = -1 EINVAL
readlink("/var/www/<app>/htdocs/releases/<ts>", 0x7fffec862410, 1023) = -1 EINVAL
... (the full readlink() chain is repeated a second time)
write(27, "... [alert] ... no buffer space in script copy ...", 198) = 198
writev(3, [..., "HTTP/1.1 500 Internal Server Error"..., "<hr><center>nginx</center>"...], 3) = 413
close(3)
Working case, same request with $document_root
Zero readlink() calls, normal FastCGI exchange:
recvfrom(4, "GET /health_check.php HTTP/1.1\r\n"..., 1024, 0, NULL, NULL) = 70
newfstatat(AT_FDCWD, "/var/www/<app>/htdocs/current/pub/health_check.php", {...}, 0) = 0
socket(AF_UNIX, SOCK_STREAM, 0) = 6
connect(6, {sa_family=AF_UNIX, sun_path="/run/php-fpm/<app>.sock"}, 110) = 0
writev(6, [{iov_base="\1\1\0\1\0\10\0\0\0\1\0\0\0\0\0\0\1\4\0\1\2\343\5\0\0104PHP_FL"..., iov_len=784}], 1) = 784
recvfrom(6, "\1\6\0\1\0*\6\0Content-type: text/html;"..., 32768, 0, NULL, NULL) = 72
readv(6, [{iov_base="", iov_len=32696}], 1) = 0
writev(4, [{iov_base="HTTP/1.1 200 OK\r\nServer: nginx\r\n"..., iov_len=254}, {iov_base="0\r\n\r\n", iov_len=5}], 2) = 259
close(4)
Interpretation
The strace output makes the root cause clear:
-
$realpath_root triggers symlink resolution. The readlink() syscalls visible in the failing trace are the kernel resolving each path component of the docroot that is how $realpath_root works under the hood: it walks the path and resolves every symlink to produce the final absolute path. On the working trace ($document_root), there are zero readlink() calls because $document_root simply uses the configured path string as-is.
-
The resolved path breaks nginx’s internal buffer. Once the symlink is resolved, the resulting SCRIPT_FILENAME value (e.g. /var/www/<app>/htdocs/releases/<ts>/pub/health_check.php) is longer than the original symlinked path. The el9_8.6 rebuild apparently changed how nginx sizes or handles that internal buffer leading to the “no buffer space in script copy” alert. The request never reaches php-fpm (no connect() to the FPM socket in the trace); nginx fails internally before it can even build the FastCGI request.
-
The symlink is stable. Verified with readlink -f, namei -l, and inotifywait -m on the parent directory no modification, no redeployment happening during the failures.
-
php-fpm is healthy. Pool up, socket valid, no FPM or kernel errors. Restarting php-fpm does not fix the issue confirming the problem is entirely on the nginx side.
Workaround
Replace $realpath_root with $document_root in the SCRIPT_FILENAME parameter:
# Before (broken)
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
# After (works)
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
$document_root passes the configured path as-is without resolving symlinks, which avoids the bug entirely.